upda/server/web/src/providers/LocaleContextProvider.tsx
Varakh 5c089989cf
All checks were successful
/ build (push) Successful in 5m22s
feat(i18n): Add ability to translate the entire page including momentjs and Ant Design (#50)
Co-authored-by: Varakh <varakh@varakh.de>
Co-committed-by: Varakh <varakh@varakh.de>
2024-12-01 10:38:43 +00:00

111 lines
3.1 KiB
TypeScript

import Languages from '../constants/languages';
import Locales from '../constants/locales';
import { Col, ConfigProvider, Layout, Row, Spin } from 'antd';
import { Locale } from 'antd/lib/locale';
import en_US from 'antd/lib/locale/en_US';
// vite cannot bundle locales of moment, see https://github.com/moment/moment/issues/5926
import moment from 'moment-timezone';
import React, { createContext, FC, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
export type LocaleProviderContextType = {
language: string;
locale: string;
};
type LocaleProviderProviderProps = {
children: ReactNode | ReactNode[];
};
const LocaleContext = createContext<LocaleProviderContextType | undefined>(undefined);
const LocaleContextProvider: FC<LocaleProviderProviderProps> = ({ children }): React.JSX.Element => {
const { i18n, t } = useTranslation();
const [antLocale, setAntLocale] = useState<Locale>();
const [language, setLanguage] = useState<string>(Languages.DEFAULT);
const [locale, setLocale] = useState<string>(Locales.DEFAULT);
const isLocale = useCallback((i18nLanguage: string) => {
return i18nLanguage.includes('-');
}, []);
const getLocaleFromLanguage = useCallback((language: string) => {
if (Languages.ENGLISH === language) {
return Locales.ENGLISH_US;
}
return Locales.DEFAULT;
}, []);
const getLanguageFromLocale = useCallback((locale: string) => {
const language = locale.split('-')[0];
if (Languages.ENGLISH === language) {
return Languages.ENGLISH;
}
return Languages.DEFAULT;
}, []);
const setLocales = useCallback(() => {
let i18nLanguage = i18n.resolvedLanguage;
let locale: string;
let language: string;
if (!i18nLanguage) {
i18nLanguage = Languages.DEFAULT;
}
if (isLocale(i18nLanguage)) {
language = getLanguageFromLocale(i18nLanguage);
locale = getLocaleFromLanguage(language);
} else {
locale = getLocaleFromLanguage(i18nLanguage);
language = getLanguageFromLocale(locale);
}
if (Languages.ENGLISH === language) {
setAntLocale(en_US);
moment.updateLocale(language, null);
} else {
setAntLocale(en_US);
}
setLocale(locale);
setLanguage(language);
}, [getLanguageFromLocale, getLocaleFromLanguage, i18n.resolvedLanguage, isLocale]);
const renderProvider = useMemo(() => {
return (
<LocaleContext.Provider value={{ locale: locale, language: language }}>
<ConfigProvider locale={antLocale}>{children}</ConfigProvider>
</LocaleContext.Provider>
);
}, [antLocale, children, language, locale]);
useEffect(() => {
setLocales();
}, [setLocales, i18n]);
if (language && locale) {
return renderProvider;
}
return (
<Layout>
<Layout.Content>
<Row align="middle">
<Col span={24}>
<Spin tip={t('loading')} spinning />
</Col>
</Row>
</Layout.Content>
</Layout>
);
};
export const useLocaleProviderContext = (): LocaleProviderContextType => {
const context = useContext(LocaleContext);
if (context === undefined) {
throw new Error('useLocaleProviderContext must be used within LocaleProvider');
}
return context;
};
export default LocaleContextProvider;