Skip to content

Commit

Permalink
Merge pull request #14 from martapanc/feat/i18n
Browse files Browse the repository at this point in the history
Feat/i18n
  • Loading branch information
martapanc authored Aug 27, 2023
2 parents a0e28d2 + c2d0ac9 commit 4a01606
Show file tree
Hide file tree
Showing 23 changed files with 342 additions and 34 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@
"@apollo/client": "^3.8.1",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@heroicons/react": "^2.0.18",
"@mui/material": "^5.13.5",
"@types/styled-components": "^5.1.0",
"classnames": "^2.3.2",
"clsx": "^2.0.0",
"focus-trap-react": "^10.1.4",
"framer-motion": "^10.12.18",
"framer-motion": "^10.16.1",
"graphql": "^16.8.0",
"i18next": "^23.4.5",
"inquirer-fuzzy-path": "^2.3.0",
Expand Down
2 changes: 1 addition & 1 deletion src/app/(public)/about/work/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Image from 'next/image';
import * as React from 'react';
import ReactMarkdown from 'react-markdown';

import Heading from '@/components/atoms/Heading';
import Heading from '@/components/atoms/headings/Heading';
import Education from '@/components/organisms/about-work/Education';
import Intro from '@/components/organisms/about-work/Intro';
import Languages from '@/components/organisms/about-work/Languages';
Expand Down
19 changes: 13 additions & 6 deletions src/app/(public)/layout-client.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
'use client';

import { ThemeProvider } from 'next-themes';
import { ThemeProvider as NextThemeProvider } from 'next-themes';
import type { PropsWithChildren } from 'react';

import MuiThemeProvider from '@/components/helpers/MuiThemeProvider';
import Footer from '@/components/layout/Footer';
import Header from '@/components/layout/Header';

export default function LayoutClient({ children }: PropsWithChildren) {
return (
<ThemeProvider attribute='class' defaultTheme='light' enableSystem={false}>
<Header />
<NextThemeProvider
attribute='class'
defaultTheme='light'
enableSystem={false}
>
<MuiThemeProvider>
<Header />

<main id='content'>{children}</main>
<main id='content'>{children}</main>

<Footer />
</ThemeProvider>
<Footer />
</MuiThemeProvider>
</NextThemeProvider>
);
}
89 changes: 89 additions & 0 deletions src/components/atoms/LanguageSwitcher/LanguageSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { InputLabel, MenuItem, Select, SelectChangeEvent } from '@mui/material';
import FormControl from '@mui/material/FormControl';
import * as React from 'react';
import { useEffect, useState } from 'react';
import { HiLanguage } from 'react-icons/hi2';

import i18n from '@/app/(public)/i18n';

export interface LanguageSwitcherProps {
languages: LanguageDef[];
}

export interface LanguageDef {
label: string;
value: string;
disabled?: boolean;
}

const LanguageSwitcher = ({ languages }: LanguageSwitcherProps) => {
const [_, setMounted] = useState(false);

// When mounted on client, now we can show the UI
useEffect(() => setMounted(true), []);

const [chosenLanguage, setChosenLanguage] = useState('');

const handleChange = (event: SelectChangeEvent) => {
const newLang = event.target.value;
setChosenLanguage(newLang);

i18n.changeLanguage(newLang);
};

const label = <HiLanguage />;

return (
<div>
<FormControl
size='small'
className='ring-offset-2'
sx={{
m: 1,
minWidth: 95,
'& .MuiFormLabel-root': {
fontSize: '1.5rem',
},
'& .MuiOutlinedInput-notchedOutline': {
borderWidth: '2px',
borderColor: '#E5E7EB',
borderRadius: '0.375rem',
},
'&:hover .MuiOutlinedInput-notchedOutline': {
borderColor: '#2562E8 !important',
},
}}
>
<InputLabel id='demo-select-small-label'>{label}</InputLabel>
<Select
id='demo-select-small'
labelId='demo-select-small-label'
value={chosenLanguage}
label={label}
onChange={handleChange}
sx={{
'& .MuiFormLabel-root': {
fontSize: '1.5rem',
},
}}
>
{languages.map(
(lang: LanguageDef) =>
!lang.disabled && (
<MenuItem
className='font-light'
style={{ fontSize: '14px', fontWeight: 300 }}
key={lang.value}
value={lang.value}
>
{lang.label}
</MenuItem>
),
)}
</Select>
</FormControl>
</div>
);
};

export default LanguageSwitcher;
53 changes: 53 additions & 0 deletions src/components/atoms/LanguageSwitcher/LanguageSwitcherMobile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import Image from 'next/image';
import * as React from 'react';
import { useEffect, useState } from 'react';

import i18n from '@/app/(public)/i18n';

export interface LanguageSwitcherProps {
languages: LanguageDef[];
}

export interface LanguageDef {
label: string;
value: string;
flag: string;
disabled?: boolean;
}

const LanguageSwitcherMobile = ({ languages }: LanguageSwitcherProps) => {
const [_, setMounted] = useState(false);

// When mounted on client, now we can show the UI
useEffect(() => setMounted(true), []);

const [__, setChosenLanguage] = useState('');

const handleChange = (newLang: string) => {
setChosenLanguage(newLang);
i18n.changeLanguage(newLang);
};

const flagSize = 20;

return (
<div className='flex flex-row'>
{languages.map(
(lang) =>
!lang.disabled && (
<Image
key={lang.value}
className='mx-2 hover:drop-shadow-md'
src={lang.flag}
alt={lang.value}
height={flagSize}
width={flagSize * 1.5}
onClick={() => handleChange(lang.value)}
/>
),
)}
</div>
);
};

export default LanguageSwitcherMobile;
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Meta } from '@storybook/react';

import LanguageSwitcher, {
LanguageDef,
LanguageSwitcherProps,
} from '@/components/atoms/LanguageSwitcher/LanguageSwitcher';

const meta: Meta<typeof LanguageSwitcher> = {
title: 'LanguageSwitcher',
component: LanguageSwitcher,
tags: ['autodocs'],
};

export default meta;

const languages: LanguageDef[] = [
{
label: '🇬🇧 EN',
value: 'en',
},
{
label: '🇮🇹 IT',
value: 'it',
},
{
label: '🇩🇪 DE',
value: 'de',
},
{
label: '🇫🇷 FR',
value: 'fr',
},
{
label: '🇪🇸 ES',
value: 'es',
disabled: true,
},
];

export const SampleStory = (args: LanguageSwitcherProps) => {
return <LanguageSwitcher {...args} />;
};
SampleStory.args = {
languages,
};
File renamed without changes.
File renamed without changes.
30 changes: 18 additions & 12 deletions src/components/buttons/ModeToggleButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,23 @@ export default function ModeToggleButton({
}

return (
<button
className={clsx(
'flex items-center justify-center rounded-md border-2 ' +
'border-grey-300 ring-grey-300 bg-transparent p-2 transition-all hover:ring-2 hover:ring-offset-2 ' +
'dark:border-grey-700 dark:ring-grey-200 dark:bg-transparent dark:hover:ring-2 dark:hover:ring-offset-2',
className,
)}
{...rest}
onClick={toggleMode}
>
{mounted ? <>{theme === 'light' ? <FiMoon /> : <FiSun />}</> : <FiSun />}
</button>
<div className='p-2'>
<button
className={clsx(
'flex items-center justify-center rounded-md border-2 ' +
'border-grey-300 ring-grey-300 bg-transparent p-2 transition-all hover:ring-2 hover:ring-offset-2 ' +
'dark:border-grey-700 dark:ring-grey-200 dark:bg-transparent dark:hover:ring-2 dark:hover:ring-offset-2',
className,
)}
{...rest}
onClick={toggleMode}
>
{mounted ? (
<>{theme === 'light' ? <FiMoon /> : <FiSun />}</>
) : (
<FiSun />
)}
</button>
</div>
);
}
27 changes: 27 additions & 0 deletions src/components/helpers/MuiThemeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { GlobalStyles } from '@mui/material';
import { CssBaseline, ThemeProvider } from '@mui/material';
import { useTheme } from 'next-themes';
import { FC, useEffect, useState } from 'react';

import { darkTheme, globalStyles, lightTheme } from '@/theme';

const MUIThemeProvider: FC<{ children: React.ReactNode }> = ({ children }) => {
const { resolvedTheme } = useTheme();
const [currentTheme, setCurrentTheme] = useState(lightTheme);

useEffect(() => {
resolvedTheme === 'light'
? setCurrentTheme(lightTheme)
: setCurrentTheme(darkTheme);
}, [resolvedTheme]);

return (
<ThemeProvider theme={currentTheme}>
<CssBaseline />
<GlobalStyles styles={globalStyles} />
{children}
</ThemeProvider>
);
};

export default MUIThemeProvider;
11 changes: 10 additions & 1 deletion src/components/layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ import { AiFillCaretDown, AiFillCaretUp } from 'react-icons/ai';
import { useOnKeyDown } from '@/hooks/useOnKeyDown';

import { BurgerIcon } from '@/components/atoms/BurgerIcon';
import LanguageSwitcher, {
LanguageDef,
} from '@/components/atoms/LanguageSwitcher/LanguageSwitcher';
import ModeToggleButton from '@/components/buttons/ModeToggleButton';
import UnstyledLink from '@/components/links/UnstyledLink';
import { MobileMenu } from '@/components/molecules/MobileMenu/MobileMenu';

import languages from '@/locales/languages.json';

export const links = [
{ href: '/projects', label: 'headerMenu.projects' },
{ href: '/cv', label: 'headerMenu.cv' },
Expand All @@ -27,6 +32,8 @@ export const links = [
export default function Header() {
const { t } = useTranslation();

const languageDefs = languages as LanguageDef[];

const [isOpen, setIsOpen] = useState(false);

const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
Expand Down Expand Up @@ -115,7 +122,9 @@ export default function Header() {
))}
</ul>

<div className='hidden md:block'>
<div className='hidden md:flex md:flex-row'>
<LanguageSwitcher languages={languageDefs} />

<ModeToggleButton />
</div>
</nav>
Expand Down
12 changes: 12 additions & 0 deletions src/components/molecules/MobileMenu/MobileMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import { AnimatePresence, motion } from 'framer-motion';
import * as React from 'react';
import { useTranslation } from 'react-i18next';

import LanguageSwitcherMobile from '@/components/atoms/LanguageSwitcher/LanguageSwitcherMobile';
import { NavigationItem } from '@/components/atoms/NavigationItem';
import ModeToggleButton from '@/components/buttons/ModeToggleButton';
import { links } from '@/components/layout/Header';

import languages from '@/locales/languages.json';

export interface MobileMenuProps {
isOpen: boolean;
}
Expand Down Expand Up @@ -66,6 +69,15 @@ export const MobileMenu = ({ isOpen }: MobileMenuProps) => {
>
<ModeToggleButton />
</motion.li>
<motion.li
className='flex justify-center'
variants={navigationVariants}
initial='hidden'
animate='visible'
custom={0.5 + (links.length + 2) * 0.1}
>
<LanguageSwitcherMobile languages={languages} />
</motion.li>
</ul>
</FocusTrap>
</motion.div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/organisms/about-work/Education.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Image from 'next/image';
import * as React from 'react';
import ReactMarkdown from 'react-markdown';

import SectionHeading from '@/components/atoms/SectionHeading';
import SectionHeading from '@/components/atoms/headings/SectionHeading';

import { School } from '@/types/School';

Expand Down
2 changes: 1 addition & 1 deletion src/components/organisms/about-work/Intro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import * as React from 'react';
import ReactMarkdown from 'react-markdown';

import SectionHeading from '@/components/atoms/SectionHeading';
import SectionHeading from '@/components/atoms/headings/SectionHeading';

import { SoftwareDevIntro } from '@/types/ShortText';

Expand Down
Loading

1 comment on commit 4a01606

@vercel
Copy link

@vercel vercel bot commented on 4a01606 Aug 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.