import React, { MouseEvent, useCallback, useState, useRef, useContext } from 'react'; import LanguageIcon from '@material-ui/icons/Language'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import i18next, { TFunction } from 'i18next'; import { useTranslation } from 'react-i18next'; import { withStyles } from '@material-ui/core/styles'; import ClickAwayListener from '@material-ui/core/ClickAwayListener'; import Grow from '@material-ui/core/Grow'; import Popper from '@material-ui/core/Popper'; import MenuList from '@material-ui/core/MenuList'; import styled from '@emotion/styled'; import { Language } from '../../../i18n/config'; import ThemeContext from '../../design-tokens/ThemeContext'; import Paper from '../../muiComponents/Paper'; import MenuItem from '../../muiComponents/MenuItem'; import Button from '../../muiComponents/Button'; import Tooltip from '../../muiComponents/Tooltip'; import Divider from '../../muiComponents/Divider'; import Box from '../../muiComponents/Box'; import { Theme } from '../../design-tokens/theme'; import Link from '../Link'; import Icon from '../Icon'; const VERDACCIO_UI_GITHUB_REPOSITORY = 'https://github.com/verdaccio/ui'; const getTranslatedCurrentLanguageDetails = ( t: TFunction, currentLanguage: string ): { translation: string; icon: React.ComponentProps['name'] } => { switch (currentLanguage) { case 'fr-FR': return { translation: t('lng.french'), icon: 'france', }; case 'pt-BR': return { translation: t('lng.portuguese'), icon: 'brazil', }; case 'de-DE': return { translation: t('lng.german'), icon: 'germany', }; case 'es-ES': return { translation: t('lng.spanish'), icon: 'spain', }; case 'zh-CN': return { translation: t('lng.chinese'), icon: 'china', }; case 'ja-JP': return { translation: t('lng.japanese'), icon: 'japan', }; case 'uk-UA': return { translation: t('lng.ukraine'), icon: 'ukraine', }; default: return { translation: t('lng.english'), icon: 'usa', }; } }; const LanguageSwitch = () => { const themeContext = useContext(ThemeContext); const { t } = useTranslation(); const [open, setOpen] = useState(false); const anchorRef = useRef(null); if (!themeContext) { throw Error(t('theme-context-not-correct-used')); } const languages = (i18next.options.resources ? Object.keys(i18next.options.resources) : []) as Array; const currentLanguage = themeContext.language; const { translation: userLanguage } = getTranslatedCurrentLanguageDetails(t, currentLanguage); const handleToggle = useCallback(() => { setOpen(prevOpen => !prevOpen); }, []); const handleClose = useCallback( (event: MouseEvent) => { if (anchorRef.current) { if (anchorRef.current.contains(event.currentTarget)) { return; } } setOpen(false); }, [setOpen] ); const handleSwitchLanguage = useCallback( (language: Language) => (event: MouseEvent) => { themeContext.setLanguage(language); handleClose(event); }, [handleClose, themeContext] ); // return focus to the button when we transitioned from !open -> open const prevOpen = React.useRef(open); React.useEffect(() => { if (prevOpen.current === true && open === false) { if (anchorRef.current) { anchorRef.current.focus(); } } prevOpen.current = open; }, [open]); return ( {userLanguage} {({ TransitionProps }) => ( {languages .filter(language => language !== currentLanguage) .map(language => { const { icon, translation } = getTranslatedCurrentLanguageDetails(t, language); return ( {translation} ); })} {`${t('help-to-translate')}`} )} ); }; export default LanguageSwitch; const Wrapper = styled('div')<{ theme?: Theme }>(({ theme }) => ({ display: 'none', [`@media screen and (min-width: ${theme && theme.breakPoints.medium}px)`]: { display: 'inline-block', }, })); const UserLanguage = styled('span')<{ theme?: Theme }>(({ theme }) => ({ display: 'none', [`@media screen and (min-width: ${theme && theme.breakPoints.medium}px)`]: { display: 'inline-block', }, })); const SwitchButton = withStyles((theme: Theme) => ({ label: { display: 'grid', gridGap: theme?.spacing(1), gridTemplateColumns: '24px 20px', [`@media screen and (min-width: ${theme && theme.breakPoints.medium}px)`]: { gridTemplateColumns: '24px 1fr 20px', }, }, }))(Button); const StyledMenuItem = withStyles((theme: Theme) => ({ root: { display: 'grid', cursor: 'pointer', gridGap: theme?.spacing(0.5), gridTemplateColumns: '20px 1fr', alignItems: 'center', '&:first-child': { borderTopLeftRadius: 4, borderTopRightRadius: 4, }, }, }))(MenuItem); const StyledLink = styled(Link)<{ theme?: Theme }>(({ theme }) => ({ color: theme?.palette.type === 'dark' ? theme?.palette.white : theme?.palette.text.primary, textDecoration: 'none', })); const StyledPaper = styled(Paper)<{ theme?: Theme }>(({ theme }) => ({ backgroundColor: theme?.palette.type === 'dark' ? theme?.palette.cyanBlue : theme?.palette.white, }));