Skip to content

Commit

Permalink
feat: add nextjs app router example
Browse files Browse the repository at this point in the history
  • Loading branch information
vonovak committed May 28, 2024
1 parent 5ed40e2 commit e5f5201
Show file tree
Hide file tree
Showing 17 changed files with 305 additions and 70 deletions.
4 changes: 4 additions & 0 deletions examples/nextjs-swc/src/app/[lang]/app-router-demo/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { HomePage } from '../../../components/HomePage'
import { withLinguiPage } from '../../../withLingui'

export default withLinguiPage(HomePage)
38 changes: 38 additions & 0 deletions examples/nextjs-swc/src/app/[lang]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import linguiConfig from '../../../lingui.config'
import { allI18nInstances, allMessages } from '../../appRouterI18n'
import { LinguiClientProvider } from '../../components/LinguiClientProvider'
import { PageLangParam, withLinguiLayout } from '../../withLingui'
import React from 'react'
import { t } from '@lingui/macro'

export async function generateStaticParams() {
return linguiConfig.locales.map((lang) => ({ lang }))
}

export function generateMetadata({ params }: PageLangParam) {
const i18n = allI18nInstances[params.lang]!

return {
title: t(i18n)`Translation Demo`
}
}

export default withLinguiLayout(function RootLayout({
children,
params: { lang }
}) {
return (
<html lang={lang}>
<body className="bg-background text-foreground">
<main className="min-h-screen flex flex-col">
<LinguiClientProvider
initialLocale={lang}
initialMessages={allMessages[lang]!}
>
{children}
</LinguiClientProvider>
</main>
</body>
</html>
)
})
9 changes: 9 additions & 0 deletions examples/nextjs-swc/src/app/[lang]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function Index() {
return (
<>
This is the homepage of the demo app. This page is not localized. You can
go to the <a href="/app-router-demo">App router demo</a> or the{' '}
<a href="/pages-router-demo">Pages router demo</a>.
</>
)
}
37 changes: 37 additions & 0 deletions examples/nextjs-swc/src/appRouterI18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'server-only'

import linguiConfig from '../lingui.config'
import { I18n, Messages, setupI18n } from '@lingui/core'

const { locales } = linguiConfig
// optionally use a stricter union type
type SupportedLocales = string

async function loadCatalog(locale: SupportedLocales): Promise<{
[k: string]: Messages
}> {
const { messages } = await import(`./locales/${locale}.po`)
return {
[locale]: messages
}
}
const catalogs = await Promise.all(locales.map(loadCatalog))

// transform array of catalogs into a single object
export const allMessages = catalogs.reduce((acc, oneCatalog) => {
return { ...acc, ...oneCatalog }
}, {})

type AllI18nInstances = { [K in SupportedLocales]: I18n }

export const allI18nInstances: AllI18nInstances = locales.reduce(
(acc, locale) => {
const messages = allMessages[locale] ?? {}
const i18n = setupI18n({
locale,
messages: { [locale]: messages }
})
return { ...acc, [locale]: i18n }
},
{}
)
3 changes: 3 additions & 0 deletions examples/nextjs-swc/src/components/Developers.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
'use client'
// this is a client component because it uses the `useState` hook

import { useState } from 'react'
import { Trans, Plural } from '@lingui/macro'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,14 @@
import { t, Trans } from '@lingui/macro'
import { GetStaticProps, NextPage } from 'next'
import React from 'react'
import { useLingui } from '@lingui/react'
import Head from 'next/head'
import { AboutText } from '../components/AboutText'
import Developers from '../components/Developers'
import { Switcher } from '../components/Switcher'
import { t, Trans } from '@lingui/macro'
import { Switcher } from './Switcher'
import { AboutText } from './AboutText'
import Developers from './Developers'
import styles from '../styles/Index.module.css'
import { loadCatalog } from '../utils'
import { useLingui } from '@lingui/react'

export const getStaticProps: GetStaticProps = async (ctx) => {
const translation = await loadCatalog(ctx.locale!)
return {
props: {
translation
}
}
}

const Index: NextPage = () => {
/**
* This hook is needed to subscribe your
* component for changes if you use t`` macro
*/
useLingui()
export const HomePage = () => {
const { i18n } = useLingui()

return (
<div className={styles.container}>
Expand All @@ -32,7 +18,7 @@ const Index: NextPage = () => {
component tree and React Context is not being passed down to the components placed in the <Head>.
That means we cannot use the <Trans> component here and instead have to use `t` macro.
*/}
<title>{t`Translation Demo`}</title>
<title>{t(i18n)`Translation Demo`}</title>
<link rel="icon" href="/favicon.ico" />
</Head>

Expand All @@ -51,5 +37,3 @@ const Index: NextPage = () => {
</div>
)
}

export default Index
25 changes: 25 additions & 0 deletions examples/nextjs-swc/src/components/LinguiClientProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client'

import { I18nProvider } from '@lingui/react'
import { type Messages, setupI18n } from '@lingui/core'
import { useState } from 'react'

type Props = {
children: React.ReactNode
initialLocale: string
initialMessages: Messages
}

export function LinguiClientProvider({
children,
initialLocale,
initialMessages
}: Props) {
const [i18n] = useState(() => {
return setupI18n({
locale: initialLocale,
messages: { [initialLocale]: initialMessages }
})
})
return <I18nProvider i18n={i18n}>{children}</I18nProvider>
}
22 changes: 14 additions & 8 deletions examples/nextjs-swc/src/components/Switcher.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import { useRouter } from 'next/router'
'use client'
// this is a client component because it uses the `useState` hook

import { useState } from 'react'
import { t, msg } from '@lingui/macro'
import { MessageDescriptor } from '@lingui/core/src'
import { msg } from '@lingui/macro'
import { useLingui } from '@lingui/react'
import { usePathname, useRouter } from 'next/navigation'

type LOCALES = 'en' | 'sr' | 'es' | 'pseudo'

const languages: { [key: string]: MessageDescriptor } = {
const languages = {
en: msg`English`,
sr: msg`Serbian`,
es: msg`Spanish`
}
} as const

export function Switcher() {
const router = useRouter()
const { i18n } = useLingui()
const pathname = usePathname()

const [locale, setLocale] = useState<LOCALES>(
router.locale!.split('-')[0] as LOCALES
pathname?.split('/')[1] as LOCALES
)

// disabled for DEMO - so we can demonstrate the 'pseudo' locale functionality
Expand All @@ -28,16 +31,19 @@ export function Switcher() {
function handleChange(event: React.ChangeEvent<HTMLSelectElement>) {
const locale = event.target.value as LOCALES

const pathNameWithoutLocale = pathname?.split('/')?.slice(2) ?? []
const newPath = `/${locale}/${pathNameWithoutLocale.join('/')}`

setLocale(locale)
router.push(router.pathname, router.pathname, { locale })
router.push(newPath)
}

return (
<select value={locale} onChange={handleChange}>
{Object.keys(languages).map((locale) => {
return (
<option value={locale} key={locale}>
{i18n._(languages[locale as unknown as LOCALES])}
{i18n._(languages[locale as keyof typeof languages])}
</option>
)
})}
Expand Down
15 changes: 8 additions & 7 deletions examples/nextjs-swc/src/locales/en.po
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ msgstr ""
"Language-Team: \n"
"Plural-Forms: \n"

#: src/components/Developers.tsx:20
#: src/components/Developers.tsx:21
msgid "{selected, plural, one {Developer} other {Developers}}"
msgstr "{selected, plural, one {Developer} other {Developers}}"

#: src/components/Switcher.tsx:10
#: src/components/Switcher.tsx:12
msgid "English"
msgstr "English"

Expand All @@ -26,22 +26,23 @@ msgstr "English"
msgid "next-explanation"
msgstr "Next.js is an open-source React front-end development web framework that enables functionality such as server-side rendering and generating static websites for React based web applications. It is a production-ready framework that allows developers to quickly create static and dynamic JAMstack websites and is used widely by many large companies."

#: src/components/Developers.tsx:9
#: src/components/Developers.tsx:10
msgid "Plural Test: How many developers?"
msgstr "Plural Test: How many developers?"

#: src/components/Switcher.tsx:11
#: src/components/Switcher.tsx:13
msgid "Serbian"
msgstr "Serbian"

#: src/components/Switcher.tsx:12
#: src/components/Switcher.tsx:14
msgid "Spanish"
msgstr "Spanish"

#: src/pages/index.tsx:28
#: src/app/[lang]/layout.tsx:16
#: src/components/HomePage.tsx:21
msgid "Translation Demo"
msgstr "Translation Demo"

#: src/pages/index.tsx:35
#: src/components/HomePage.tsx:28
msgid "Welcome to <0>Next.js!</0>"
msgstr "Welcome to <0>Next.js!</0>"
15 changes: 8 additions & 7 deletions examples/nextjs-swc/src/locales/es.po
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ msgstr ""
"Language-Team: \n"
"Plural-Forms: \n"

#: src/components/Developers.tsx:20
#: src/components/Developers.tsx:21
msgid "{selected, plural, one {Developer} other {Developers}}"
msgstr "{selected, plural, one {Programador} other {Programadores}}"

#: src/components/Switcher.tsx:10
#: src/components/Switcher.tsx:12
msgid "English"
msgstr "Inglés"

Expand All @@ -26,22 +26,23 @@ msgstr "Inglés"
msgid "next-explanation"
msgstr "Next.js es un marco de trabajo web de desarrollo front-end de React de código abierto que permite funciones como la representación del lado del servidor y la generación de sitios web estáticos para aplicaciones web basadas en React. Es un marco listo para producción que permite a los desarrolladores crear rápidamente sitios web JAMstack estáticos y dinámicos y es ampliamente utilizado por muchas grandes empresas."

#: src/components/Developers.tsx:9
#: src/components/Developers.tsx:10
msgid "Plural Test: How many developers?"
msgstr "Prueba Plural: Cuantos programadores?"

#: src/components/Switcher.tsx:11
#: src/components/Switcher.tsx:13
msgid "Serbian"
msgstr "Serbio"

#: src/components/Switcher.tsx:12
#: src/components/Switcher.tsx:14
msgid "Spanish"
msgstr "Español"

#: src/pages/index.tsx:28
#: src/app/[lang]/layout.tsx:16
#: src/components/HomePage.tsx:21
msgid "Translation Demo"
msgstr "Demostración de Traducción"

#: src/pages/index.tsx:35
#: src/components/HomePage.tsx:28
msgid "Welcome to <0>Next.js!</0>"
msgstr "Bienvenido a <0>Next.js!</0>"
15 changes: 8 additions & 7 deletions examples/nextjs-swc/src/locales/pseudo.po
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ msgstr ""
"Language-Team: \n"
"Plural-Forms: \n"

#: src/components/Developers.tsx:20
#: src/components/Developers.tsx:21
msgid "{selected, plural, one {Developer} other {Developers}}"
msgstr ""

#: src/components/Switcher.tsx:10
#: src/components/Switcher.tsx:12
msgid "English"
msgstr ""

Expand All @@ -26,22 +26,23 @@ msgstr ""
msgid "next-explanation"
msgstr ""

#: src/components/Developers.tsx:9
#: src/components/Developers.tsx:10
msgid "Plural Test: How many developers?"
msgstr ""

#: src/components/Switcher.tsx:11
#: src/components/Switcher.tsx:13
msgid "Serbian"
msgstr ""

#: src/components/Switcher.tsx:12
#: src/components/Switcher.tsx:14
msgid "Spanish"
msgstr ""

#: src/pages/index.tsx:28
#: src/app/[lang]/layout.tsx:16
#: src/components/HomePage.tsx:21
msgid "Translation Demo"
msgstr ""

#: src/pages/index.tsx:35
#: src/components/HomePage.tsx:28
msgid "Welcome to <0>Next.js!</0>"
msgstr ""
Loading

0 comments on commit e5f5201

Please sign in to comment.