✨(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:
@@ -12,8 +12,8 @@ const config = {
|
|||||||
MEDIA_BASE_URL: 'http://localhost:8083',
|
MEDIA_BASE_URL: 'http://localhost:8083',
|
||||||
LANGUAGES: [
|
LANGUAGES: [
|
||||||
['en-us', 'English'],
|
['en-us', 'English'],
|
||||||
['fr-fr', 'French'],
|
['fr-fr', 'Français'],
|
||||||
['de-de', 'German'],
|
['de-de', 'Deutsch'],
|
||||||
],
|
],
|
||||||
LANGUAGE_CODE: 'en-us',
|
LANGUAGE_CODE: 'en-us',
|
||||||
POSTHOG_KEY: {},
|
POSTHOG_KEY: {},
|
||||||
|
|||||||
@@ -4,25 +4,45 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { css } from 'styled-components';
|
import { css } from 'styled-components';
|
||||||
|
|
||||||
import { DropdownMenu, Text } from '@/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 = () => {
|
export const LanguagePicker = () => {
|
||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const { preload: languages } = i18n.options;
|
const { data: conf } = useConfig();
|
||||||
const language = i18n.language;
|
const { synchronizeLanguage } = useLanguageSynchronizer();
|
||||||
|
const language = i18n.languages[0];
|
||||||
Settings.defaultLocale = language;
|
Settings.defaultLocale = language;
|
||||||
|
|
||||||
|
// Compute options for dropdown
|
||||||
const optionsPicker = useMemo(() => {
|
const optionsPicker = useMemo(() => {
|
||||||
return (languages || []).map((lang) => ({
|
const backendOptions = conf?.LANGUAGES ?? [[language, language]];
|
||||||
label: LANGUAGES_ALLOWED[lang],
|
return backendOptions.map(([backendLocale, label]) => {
|
||||||
isSelected: language === lang,
|
// Determine if the option is selected
|
||||||
callback: () => {
|
const isSelected =
|
||||||
i18n.changeLanguage(lang).catch((err) => {
|
getMatchingLocales([backendLocale], [language]).length > 0;
|
||||||
console.error('Error changing language', err);
|
// Define callback for updating both frontend and backend languages
|
||||||
});
|
const callback = () => {
|
||||||
},
|
i18n
|
||||||
}));
|
.changeLanguage(backendLocale)
|
||||||
}, [i18n, language, languages]);
|
.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 (
|
return (
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
@@ -54,7 +74,7 @@ export const LanguagePicker = () => {
|
|||||||
<Text $isMaterialIcon $color="inherit" $size="xl">
|
<Text $isMaterialIcon $color="inherit" $size="xl">
|
||||||
translate
|
translate
|
||||||
</Text>
|
</Text>
|
||||||
{LANGUAGES_ALLOWED[language]}
|
{currentLanguageLabel}
|
||||||
</Text>
|
</Text>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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';
|
|
||||||
@@ -1,27 +1,34 @@
|
|||||||
import i18n from 'i18next';
|
import i18next from 'i18next';
|
||||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||||
import { initReactI18next } from 'react-i18next';
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
|
||||||
import { BASE_LANGUAGE, LANGUAGES_ALLOWED, LANGUAGE_COOKIE_NAME } from './conf';
|
|
||||||
import resources from './translations.json';
|
import resources from './translations.json';
|
||||||
|
|
||||||
i18n
|
export const availableFrontendLanguages: readonly string[] =
|
||||||
|
Object.keys(resources);
|
||||||
|
|
||||||
|
i18next
|
||||||
.use(LanguageDetector)
|
.use(LanguageDetector)
|
||||||
.use(initReactI18next)
|
.use(initReactI18next)
|
||||||
.init({
|
.init({
|
||||||
resources,
|
resources,
|
||||||
fallbackLng: BASE_LANGUAGE,
|
fallbackLng: 'en',
|
||||||
supportedLngs: Object.keys(LANGUAGES_ALLOWED),
|
debug: false,
|
||||||
detection: {
|
detection: {
|
||||||
order: ['cookie', 'navigator'], // detection order
|
order: ['cookie', 'navigator'], // detection order
|
||||||
caches: ['cookie'], // Use cookies to store the language preference
|
caches: ['cookie'], // Use cookies to store the language preference
|
||||||
lookupCookie: LANGUAGE_COOKIE_NAME,
|
lookupCookie: 'docs_language',
|
||||||
cookieMinutes: 525600, // Expires after one year
|
cookieMinutes: 525600, // Expires after one year
|
||||||
|
cookieOptions: {
|
||||||
|
path: '/',
|
||||||
|
sameSite: 'lax',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
interpolation: {
|
interpolation: {
|
||||||
escapeValue: false,
|
escapeValue: false,
|
||||||
},
|
},
|
||||||
preload: Object.keys(LANGUAGES_ALLOWED),
|
preload: availableFrontendLanguages,
|
||||||
|
lowerCaseLng: true,
|
||||||
nsSeparator: false,
|
nsSeparator: false,
|
||||||
keySeparator: false,
|
keySeparator: false,
|
||||||
})
|
})
|
||||||
@@ -29,4 +36,4 @@ i18n
|
|||||||
throw new Error('i18n initialization failed');
|
throw new Error('i18n initialization failed');
|
||||||
});
|
});
|
||||||
|
|
||||||
export default i18n;
|
export default i18next;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
|
|
||||||
import { AppProvider } from '@/core/';
|
import { AppProvider } from '@/core/';
|
||||||
import { useSWRegister } from '@/features/service-worker/';
|
import { useSWRegister } from '@/features/service-worker/';
|
||||||
|
import '@/i18n/initI18n';
|
||||||
import { NextPageWithLayout } from '@/types/next';
|
import { NextPageWithLayout } from '@/types/next';
|
||||||
|
|
||||||
import './globals.css';
|
import './globals.css';
|
||||||
|
|||||||
Reference in New Issue
Block a user