Skip to content

Commit

Permalink
Merge pull request #41 from romros/multilanguage-next-i18next
Browse files Browse the repository at this point in the history
Multilanguage next i18next
  • Loading branch information
hta218 authored Aug 13, 2023
2 parents b418b92 + 7a67580 commit ce6c9a5
Show file tree
Hide file tree
Showing 189 changed files with 12,023 additions and 421 deletions.
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ GISCUS_CATEGORY=
GISCUS_CATEGORY_ID=
UTTERANCES_REPO=
DISQUS_SHORTNAME=
NEXT_PUBLIC_CREATE_DISCUS_ON_TWITTER=FALSE
NEXT_PUBLIC_CREATE_DISCUS_ON_GITHUB=FALSE
NEXT_PUBLIC_SHARE_ON_FACEBOOK=FALSE
NEXT_PUBLIC_SHARE_ON_TWITTER=TRUE


# Spotify (for fetching now playing track)
SPOTIFY_CLIENT_ID=
Expand All @@ -16,3 +21,8 @@ DATABASE_URL=

# GitHub (for fetching repository's data in /projects page)
GITHUB_API_TOKEN=


# Default locale (for i18n)
NEXT_PUBLIC_DEFAULT_LOCALE=en
NEXT_PUBLIC_DEFAULT_AUTHOR=Tuan Anh Huynh
7 changes: 5 additions & 2 deletions components/BuiltWith.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { siteMetadata } from '~/data/siteMetadata'
import { DevIcon } from './DevIcon'
import { Link } from './Link'
import { useTranslation } from 'next-i18next'

export function BuiltWith() {
const { t } = useTranslation('common')

return (
<div className="flex items-center space-x-1">
<span className="mr-1 text-gray-500 dark:text-gray-400">Built with</span>
<span className="mr-1 text-gray-500 dark:text-gray-400">{t('buildWith.built_with')}</span>
<div className="flex space-x-1.5">
<Link href="https://nextjs.org?ref=leohuynh.dev">
<DevIcon type="NextJS" className="h-5 w-5" />
Expand All @@ -28,7 +31,7 @@ export function BuiltWith() {
href={siteMetadata.siteRepo}
className="text-gray-500 underline underline-offset-4 dark:text-gray-400"
>
<span data-umami-event="view-source">View source</span>
<span data-umami-event="view-source">{t('buildWith.view_source')}</span>
</Link>
</div>
)
Expand Down
2 changes: 1 addition & 1 deletion components/DevIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export let DevIconsMap = {
export function DevIcon(props: { type: keyof typeof DevIconsMap; className?: string }) {
let { type, className } = props
let Icon = DevIconsMap[type]
if (!Icon) return <div>Missing icon</div>
if (!Icon) return <div>Missing icon for {type}</div> // Add `type` here

let defaultClass = 'h-16 w-16 lg:h-14 lg:w-14 xl:h-24 xl:w-24'
return <Icon className={className || defaultClass} fill="currentColor" />
Expand Down
6 changes: 4 additions & 2 deletions components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { siteMetadata } from '~/data/siteMetadata'
import { useTranslation } from 'next-i18next'
import { BuiltWith } from './BuiltWith'

export function Footer() {
const { t } = useTranslation('common')

return (
<footer>
<div className="mb-8 mt-16 items-center justify-between space-y-4 md:mb-10 md:flex md:space-y-0">
<BuiltWith />
<div className="my-2 flex space-x-2 text-sm text-gray-500 dark:text-gray-400">
<div>{`Copyright © ${new Date().getFullYear()}`}</div>
<span>{` • `}</span>
<span>{siteMetadata.footerTitle}</span>
<span>{t('buildWith.copyright_author')}</span>
</div>
</div>
</footer>
Expand Down
60 changes: 36 additions & 24 deletions components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import clsx from 'clsx'
import { headerNavLinks } from 'data/headerNavLinks'
import NextImage from 'next/image'
import { useRouter } from 'next/router'
import { AnalyticsLink } from './AnalyticsLink'
import { Link } from './Link'
import { ThemeSwitcher } from './ThemeSwitcher'
import { LanguageSwitcher } from './LanguageSwitcher'
import { useTranslation } from 'next-i18next'
import { headerNavLinks } from '~/data/headerNavLinks'

export function Header({ onToggleNav }: { onToggleNav: () => void }) {
const { t } = useTranslation('common') // 'common' fa referència al fitxer common.json
let router = useRouter()
return (
<header className="supports-backdrop-blur:bg-white/95 sticky top-0 z-40 overflow-x-hidden bg-white/75 py-3 backdrop-blur dark:bg-dark/75">
<header className="supports-backdrop-blur:bg-white/95 sticky top-0 z-50 overflow-visible bg-white/75 py-3 backdrop-blur dark:bg-dark/75">
<div className="mx-auto flex max-w-3xl items-center justify-between px-3 xl:max-w-5xl xl:px-0">
<div>
<div className="flex items-center justify-between">
<Link href="/" aria-label="Leo's Blog">
<div className="flex items-center justify-between" data-umami-event="logo">
<div className="mr-3 flex items-center justify-center">
Expand All @@ -25,29 +28,28 @@ export function Header({ onToggleNav }: { onToggleNav: () => void }) {
</div>
</div>
</Link>
</div>
<div className="flex items-center text-base leading-5">
<div className="hidden space-x-2 sm:block">
{headerNavLinks.map((link) => {
return (
<Link key={link.title} href={link.href}>
<span
className={clsx(
'inline-block rounded px-2 py-1 font-medium text-gray-900 dark:text-gray-100 sm:px-3 sm:py-2',
router.pathname.startsWith(link.href)
? 'bg-gray-200 dark:bg-gray-700'
: 'hover:bg-gray-200 dark:hover:bg-gray-700'
)}
data-umami-event={`nav-${link.href.replace('/', '')}`}
>
{link.title}
</span>
</Link>
)
})}
{headerNavLinks.map((link) => (
<Link key={link.titleKey} href={link.href}>
<span
className={clsx(
'inline-block rounded px-2 py-1 font-medium text-gray-900 dark:text-gray-100 sm:px-3 sm:py-2',
router.pathname.startsWith(link.href)
? 'bg-gray-200 dark:bg-gray-700'
: 'hover:bg-gray-200 dark:hover:bg-gray-700'
)}
data-umami-event={`nav-${link.href.replace('/', '')}`}
>
{t(link.titleKey)}{' '}
</span>
</Link>
))}
</div>
</div>
<div className="flex items-center">
<AnalyticsLink />
<ThemeSwitcher />
<LanguageSwitcher />
<button
className="ml-2 mr-1 h-8 w-8 rounded sm:hidden"
type="button"
Expand All @@ -63,9 +65,19 @@ export function Header({ onToggleNav }: { onToggleNav: () => void }) {
>
<path
fillRule="evenodd"
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
clipRule="evenodd"
></path>
<path
fillRule="evenodd"
d="M3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
clipRule="evenodd"
></path>
<path
fillRule="evenodd"
d="M3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
clipRule="evenodd"
/>
></path>
</svg>
</button>
</div>
Expand Down
89 changes: 89 additions & 0 deletions components/LanguageSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { useEffect, useState } from 'react'
import { useRouter } from 'next/router'

const localeNames = {
en: 'English',
es: 'Español',
ca: 'Català',
// add other languages as needed
}

function getLocaleName(localeCode) {
return localeNames[localeCode] || localeCode
}

function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1)
}
export function LanguageSwitcher() {
const router = useRouter()
const [value, setValue] = useState(router.locale)
const [dropdownOpen, setDropdownOpen] = useState(false)

useEffect(() => {
setValue(router.locale)
}, [router.locale])

function translate_to(locale) {
router.push(router.pathname, router.asPath, { locale })
setDropdownOpen(false) // Tancar el menú desplegable un cop l'usuari hagi seleccionat una opció
}

return (
<div className="relative inline-block text-left">
<button
id="dropdownDefaultButton"
data-dropdown-toggle="dropdown"
onClick={() => setDropdownOpen(!dropdownOpen)}
className=" font-medium text-gray-700 hover:bg-gray-200 rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center
dark:border-gray-600 dark:text-gray-200 dark:hover:bg-gray-700 dark:hover:text-white
"
type="button"
>
{capitalize(value)}
<svg
className="w-2.5 h-2.5 ml-2.5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 10 6"
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="m1 1 4 4 4-4"
/>
</svg>
</button>

{dropdownOpen && (
<div
id="dropdown"
className="origin-top-right absolute right-0 mt-2 w-44 rounded-lg shadow bg-white ring-1 ring-black ring-opacity-5 z-50 divide-y divide-gray-100
dark:divide-gray-600 dark:ring-gray-800 dark:ring-opacity-5 dark:ring-1 dark:bg-gray-700 dark:ring-offset-2 dark:ring-offset-gray-700"
>
<ul
className="py-2 text-sm text-gray-700 dark:text-gray-200"
aria-labelledby="dropdownDefaultButton"
>
{router.locales.map((locale) => {
const label = getLocaleName(locale)
return (
<li key={locale}>
<button
onClick={() => translate_to(locale)}
className="block px-4 py-2 w-full text-left hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
>
{label}
</button>
</li>
)
})}
</ul>
</div>
)}
</div>
)
}
22 changes: 17 additions & 5 deletions components/MobileNav.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { headerNavLinks } from '~/data/headerNavLinks'
import { Link } from './Link'
import clsx from 'clsx'
import { useTranslation } from 'next-i18next'

export function MobileNav({ navShow, onToggleNav }) {
const { t } = useTranslation('common')
let className = clsx(
`sm:hidden fixed w-full h-screen inset-0 bg-gray-200 dark:bg-gray-800 opacity-95 z-50 transition-transform transform ease-in-out duration-300`,
navShow ? 'translate-x-0' : 'translate-x-full'
Expand All @@ -23,20 +25,30 @@ export function MobileNav({ navShow, onToggleNav }) {
>
<path
fillRule="evenodd"
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
clipRule="evenodd"
/>
></path>
<path
fillRule="evenodd"
d="M3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
clipRule="evenodd"
></path>
<path
fillRule="evenodd"
d="M3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
clipRule="evenodd"
></path>
</svg>
</button>
<nav className="fixed mt-8 h-full">
<nav className="fixed mt-24 h-full">
{headerNavLinks.map((link) => (
<div key={link.title} className="px-8 py-4">
<div key={link.titleKey} className="px-8 py-4">
<Link
href={link.href}
className="text-2xl font-semibold tracking-wide text-gray-900 dark:text-gray-100"
onClick={onToggleNav}
>
{link.title}
{t(link.titleKey)}{' '}
</Link>
</div>
))}
Expand Down
2 changes: 1 addition & 1 deletion components/PageTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { PageTitleProps } from '~/types'

export function PageTitle({ children }: PageTitleProps) {
return (
<h1 className="text-[34px] font-extrabold leading-10 tracking-tight text-gray-900 dark:text-gray-100 md:text-5xl md:leading-14 lg:text-[54px] lg:leading-[64px]">
<h1 className="text-[24px] font-extrabold leading-10 tracking-tight text-gray-900 dark:text-gray-100 md:text-base md:leading-14 lg:text-[34px] lg:leading-[44px]">
{children}
</h1>
)
Expand Down
12 changes: 7 additions & 5 deletions components/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { PaginationType } from '~/types'
import { Link } from './Link'
import { useTranslation } from 'next-i18next'

export function Pagination({ totalPages, currentPage }: PaginationType) {
let hasPrevPage = currentPage - 1 > 0
let hasNextPage = currentPage + 1 <= totalPages
const { t } = useTranslation('common')

return (
<div className="space-y-2 pb-8 pt-6 md:space-y-5">
Expand All @@ -14,29 +16,29 @@ export function Pagination({ totalPages, currentPage }: PaginationType) {
disabled={!hasPrevPage}
data-umami-event="prev-posts"
>
Previous
{t('pagination.previous')}
</button>
)}
{hasPrevPage && (
<Link href={currentPage - 1 === 1 ? `/blog/` : `/blog/page/${currentPage - 1}`}>
<button>Previous</button>
<button>{t('pagination.previous')}</button>
</Link>
)}
<span>
{currentPage} of {totalPages}
{currentPage} {t('pagination.of')} {totalPages}
</span>
{!hasNextPage && (
<button
className="cursor-auto disabled:opacity-50"
disabled={!hasNextPage}
data-umami-event="next-posts"
>
Next
{t('pagination.next')}
</button>
)}
{hasNextPage && (
<Link href={`/blog/page/${currentPage + 1}`}>
<button>Next</button>
<button>{t('pagination.next')}</button>
</Link>
)}
</nav>
Expand Down
8 changes: 6 additions & 2 deletions components/PostListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@ import type { MdxFrontMatter } from '~/types'
import { formatDate } from '~/utils/date'
import { Link } from './Link'
import { Tag } from './Tag'
import { useTranslation } from 'next-i18next'
import { language } from 'gray-matter'

export function PostListItem({ frontMatter }: { frontMatter: MdxFrontMatter }) {
let { slug, date, title, summary, tags } = frontMatter
const { t, i18n } = useTranslation()
const lang = i18n.language
return (
<li key={slug}>
<article className="space-y-2 xl:grid xl:grid-cols-4 xl:items-baseline xl:space-y-0">
<dl>
<dt className="sr-only">Published on</dt>
<dt className="sr-only">{t('blog.published_on')}</dt>
<dd className="text-base font-medium leading-6 text-gray-500 dark:text-gray-400">
<time dateTime={date}>{formatDate(date)}</time>
<time dateTime={date}>{formatDate(date, lang)}</time>
</dd>
</dl>
<div className="space-y-3 xl:col-span-3">
Expand Down
Loading

1 comment on commit ce6c9a5

@vercel
Copy link

@vercel vercel bot commented on ce6c9a5 Aug 13, 2023

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

leo-huynh-dot-dev – ./

leo-huynh-dot-dev-hta218.vercel.app
leo-huynh-dot-dev-git-main-hta218.vercel.app
www.leohuynh.dev
leohuynh.dev

Please sign in to comment.