1
1
mirror of https://github.com/theoludwig/theoludwig.git synced 2025-05-29 22:37:44 +02:00

feat: rewrite to Next.js v13 app directory

Improvements:
- Hide switch theme input (ugly little white square)
- i18n without subpath (e.g: /fr or /en), same url whatever the locale used
This commit is contained in:
2023-07-31 19:06:46 +02:00
parent 5640f1b434
commit 6b29ce9b15
61 changed files with 755 additions and 787 deletions

View File

@ -1,24 +0,0 @@
import Image from 'next/image'
export interface LanguageFlagProps {
language: string
}
export const LanguageFlag: React.FC<LanguageFlagProps> = (props) => {
const { language } = props
return (
<>
<Image
quality={100}
width={35}
height={35}
src={`/images/languages/${language}.svg`}
alt={language}
/>
<p data-cy='language-flag-text' className='mx-2 text-base'>
{language.toUpperCase()}
</p>
</>
)
}

View File

@ -0,0 +1,30 @@
import Image from 'next/image'
import type { CookiesStore } from '@/i18n/i18n.client'
import { useI18n } from '@/i18n/i18n.client'
export interface LocaleFlagProps {
locale: string
cookiesStore: CookiesStore
}
export const LocaleFlag: React.FC<LocaleFlagProps> = (props) => {
const { locale, cookiesStore } = props
const i18n = useI18n(cookiesStore)
return (
<>
<Image
quality={100}
width={35}
height={35}
src={`/images/locales/${locale}.svg`}
alt={locale}
/>
<p data-cy='locale-flag-text' className='mx-2 text-base'>
{i18n.translate(`common.${locale}`)}
</p>
</>
)
}

View File

@ -1,17 +1,22 @@
'use client'
import { useCallback, useEffect, useState, useRef } from 'react'
import useTranslation from 'next-translate/useTranslation'
import setLanguage from 'next-translate/setLanguage'
import classNames from 'clsx'
import i18n from 'i18n.json'
import { AVAILABLE_LOCALES } from '@/utils/constants'
import type { CookiesStore } from '@/i18n/i18n.client'
import { Arrow } from './Arrow'
import { LanguageFlag } from './LanguageFlag'
import { LocaleFlag } from './LocaleFlag'
export interface LocalesProps {
currentLocale: string
cookiesStore: CookiesStore
}
export const Locales = (props: LocalesProps): JSX.Element => {
const { currentLocale, cookiesStore } = props
export const Language: React.FC = () => {
const { lang: currentLanguage } = useTranslation()
const [hiddenMenu, setHiddenMenu] = useState(true)
const languageClickRef = useRef<HTMLDivElement | null>(null)
@ -38,42 +43,48 @@ export const Language: React.FC = () => {
}
}, [])
const handleLanguage = async (language: string): Promise<void> => {
await setLanguage(language, false)
const handleLocale = async (locale: string): Promise<void> => {
const { setLocale } = await import('@/i18n/i18n.server')
setLocale(locale)
}
return (
<div className='flex cursor-pointer flex-col items-center justify-center'>
<div
ref={languageClickRef}
data-cy='language-click'
data-cy='locale-click'
className='mr-5 flex items-center'
onClick={handleHiddenMenu}
>
<LanguageFlag language={currentLanguage} />
<LocaleFlag
locale={currentLocale}
cookiesStore={cookiesStore?.toString()}
/>
<Arrow />
</div>
<ul
data-cy='languages-list'
data-cy='locales-list'
className={classNames(
'absolute top-14 z-10 mr-4 mt-3 flex w-24 list-none flex-col items-center justify-center rounded-lg bg-white p-0 shadow-lightFlag dark:bg-black dark:shadow-darkFlag',
'absolute top-14 z-10 mr-4 mt-3 flex w-32 list-none flex-col items-center justify-center rounded-lg bg-white p-0 shadow-lightFlag dark:bg-black dark:shadow-darkFlag',
{ hidden: hiddenMenu }
)}
>
{i18n.locales.map((language, index) => {
if (language === currentLanguage) {
return <></>
}
{AVAILABLE_LOCALES.filter((locale) => {
return locale !== currentLocale
}).map((locale) => {
return (
<li
key={index}
className='flex h-12 w-full items-center justify-center pl-2 hover:bg-[#4f545c] hover:bg-opacity-20'
key={locale}
className='flex h-12 w-full items-center justify-center hover:bg-[#4f545c] hover:bg-opacity-20'
onClick={async () => {
return await handleLanguage(language)
return await handleLocale(locale)
}}
>
<LanguageFlag language={language} />
<LocaleFlag
locale={locale}
cookiesStore={cookiesStore?.toString()}
/>
</li>
)
})}

View File

@ -71,7 +71,7 @@ export const SwitchTheme: React.FC = () => {
data-cy='switch-theme-input'
type='checkbox'
aria-label='Dark mode toggle'
className='absolute m-[-1px] h-[1px] w-[1px] overflow-hidden border-0 p-0'
className='absolute m-[-1px] h-[1px] w-[1px] overflow-hidden border-0 p-0 hidden'
defaultChecked
/>
</div>

View File

@ -1,15 +1,21 @@
import { cookies } from 'next/headers'
import Link from 'next/link'
import Image from 'next/image'
import { Language } from './Language'
import { getI18n } from '@/i18n/i18n.server'
import { Locales } from './Locales'
import { SwitchTheme } from './SwitchTheme'
export interface HeaderProps {
showLanguage?: boolean
showLocale?: boolean
}
export const Header: React.FC<HeaderProps> = (props) => {
const { showLanguage = false } = props
const { showLocale = false } = props
const cookiesStore = cookies()
const i18n = getI18n()
return (
<header className='sticky top-0 z-50 flex w-full justify-between border-b-2 border-gray-600 bg-white px-6 py-2 dark:border-gray-400 dark:bg-black'>
@ -38,7 +44,12 @@ export const Header: React.FC<HeaderProps> = (props) => {
Blog
</Link>
</div>
{showLanguage ? <Language /> : null}
{showLocale ? (
<Locales
currentLocale={i18n.locale}
cookiesStore={cookiesStore.toString()}
/>
) : null}
<SwitchTheme />
</div>
</header>