Skip to content

Commit

Permalink
add i18n and auth
Browse files Browse the repository at this point in the history
  • Loading branch information
omar2205 committed Dec 8, 2022
1 parent fe77186 commit 205843c
Show file tree
Hide file tree
Showing 28 changed files with 1,133 additions and 225 deletions.
5 changes: 5 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
NEXTAUTH_SECRET=xg2O32JwrcDRsQZZvwRO4DUQ5Jzud/DaHPsHqgTX3Kg=
#NEXTAUTH_URL=https://web.localhost
NEXTAUTH_URL=http://localhost:3000
BACKEND_URL=CHANGE ME BACKEND_URL

4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ yarn-error.log*
.pnpm-debug.log*

# local env files
.env*.local
.env*
!.env.sample
!.env.example

# vercel
.vercel
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.next
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"singleQuote": true,
"semi": false,
"trailingComma": "all",
"tabWidth": 2,
"printWidth": 80
}
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"typescript.tsdk": "node_modules/.pnpm/typescript@4.9.4/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}
12 changes: 12 additions & 0 deletions app/AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
'use client'
import { Session } from 'next-auth'
import { SessionProvider } from 'next-auth/react'

interface IAuthProviderProps {
children: React.ReactNode
session: Session | null
}

export default function({ children, session }: IAuthProviderProps) {
return <SessionProvider session={session}>{children}</SessionProvider>
}
53 changes: 53 additions & 0 deletions app/[lng]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import '../globals.css'
import { Session } from 'next-auth'
import { DM_Sans } from '@next/font/google'

import AuthProvider from '../AuthProvider'
import { languages } from '../i18n/settings'
import { useTranslation } from '../i18n'

const dm_sans = DM_Sans({
variable: '--font-dm-sans',
weight: ['400', '500', '700'],
subsets: ['latin', 'latin-ext'],
})

export async function generateStaticParams() {
return languages.map((lng) => ({ lng }))
}

type RootLayoutProps = {
children: React.ReactNode,
params: {
lng: string
}
}

async function getSession(cookie: string): Promise<Session | null> {
const response = await fetch(`${process.env.NEXTAUTH_URL}/api/auth/session`, {
headers: { cookie },
})

if (!response?.ok) {
return null
}

const session = await response.json()
return Object.keys(session).length > 0 ? session : null
}

export default async function RootLayout({ children, params }: RootLayoutProps) {
const session = await getSession('next-auth.session-token')
const { i18n } = await useTranslation(params.lng)

return (
<html lang={i18n.language} dir={i18n.dir()}>
<head />
<body className={dm_sans.variable}>
<AuthProvider session={session}>
{children}
</AuthProvider>
</body>
</html>
)
}
43 changes: 43 additions & 0 deletions app/[lng]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use client'
import { use } from 'react'
import Link from 'next/link'
import { useSession } from 'next-auth/react'
import { useTranslation } from '../i18n/client'
import { languages } from '../i18n/settings'

const Lang = ({ lng }: { lng: string }) => {
const links = languages.filter(l => l !== lng)
return <>{links.map(l => <Link key={l} href={`/${l}`}>{l}</Link>)}</>
}


export default function({ params }: PageProps) {
const { t } = useTranslation(params.lng, 'login', {})

const s = useSession()

console.log({ s })

return <>
<div
className="flex flex-col gap-4 justify-center align-center
h-screen max-w-lg mx-auto text-center"
>
<h1 className="text-3xl font-bold">{t('title')}</h1>
<p>{t('subtitle')}</p>
<Link
className="p-2 mx-auto w-48 rounded
bg-green-800 hover:bg-green-700"
href="/login"
>
{t('login')}
</Link>
</div>
<footer className="absolute top-0">
<pre>
{JSON.stringify(s.data, null, 2)}
</pre>
<Lang lng={params.lng} />
</footer>
</>
}
6 changes: 5 additions & 1 deletion app/globals.css
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
font-family: var(--font-dm-sans), -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}

Expand Down
17 changes: 17 additions & 0 deletions app/i18n/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client'

import i18next from 'i18next'
import { initReactI18next, useTranslation as useTranslationOrg } from 'react-i18next'
import resourcesToBackend from 'i18next-resources-to-backend'
import { getOptions } from './settings'

// on client side the normal singleton is ok
i18next
.use(initReactI18next)
.use(resourcesToBackend((language: string, namespace: string) => import(`./locales/${language}/${namespace}.json`)))
.init(getOptions())

export function useTranslation(lng: string, ns: string, options: any) {
if (i18next.resolvedLanguage !== lng) i18next.changeLanguage(lng)
return useTranslationOrg(ns, options)
}
23 changes: 23 additions & 0 deletions app/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createInstance } from 'i18next'
import resourcesToBackend from 'i18next-resources-to-backend'
import { initReactI18next } from 'react-i18next/initReactI18next'
import { getOptions, fallbackLng } from './settings'

const initI18next = async (lng: string, ns: string) => {
// on server side we create a new instance for each render, because during compilation everything seems to be executed in parallel
const i18nInstance = createInstance()
await i18nInstance
.use(initReactI18next)
.use(resourcesToBackend((language: string, namespace: string) => import(`./locales/${language}/${namespace}.json`)))
.init(getOptions(lng, ns))
return i18nInstance
}

export async function useTranslation(lng: string, ns = fallbackLng, options = {}) {
const i18nextInstance = await initI18next(lng, ns)
return {
// @ts-ignore
t: i18nextInstance.getFixedT(lng, ns, options.keyPrefix),
i18n: i18nextInstance
}
}
5 changes: 5 additions & 0 deletions app/i18n/locales/ar/login.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"title": "هذه منطقة محذورة",
"subtitle": "يجب عليك تسجيل الدخول للاستمرار",
"login": "تسجيل الدخول"
}
4 changes: 4 additions & 0 deletions app/i18n/locales/ar/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

{
"h1": "مرحبا"
}
5 changes: 5 additions & 0 deletions app/i18n/locales/en/login.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"title": "This is a protected page",
"subtitle": "You need to login to continue",
"login": "login"
}
3 changes: 3 additions & 0 deletions app/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"h1": "Hello world"
}
17 changes: 17 additions & 0 deletions app/i18n/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const fallbackLng = 'en'
export const languages = [fallbackLng, 'ar']
export const defaultNS = 'translation'

export function getOptions(lng = fallbackLng, ns = defaultNS) {
return {
// debug: true,
supportedLngs: languages,
// preload: languages,
fallbackLng,
lng,
fallbackNS: defaultNS,
defaultNS,
ns
}
}

18 changes: 0 additions & 18 deletions app/layout.tsx

This file was deleted.

53 changes: 53 additions & 0 deletions app/layout.tsxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import './globals.css'
import { Session } from 'next-auth'
import { DM_Sans } from '@next/font/google'

import AuthProvider from './AuthProvider'
import { languages } from './i18n/settings'
import { useTranslation } from './i18n'

const dm_sans = DM_Sans({
variable: '--font-dm-sans',
weight: ['400', '500', '700'],
subsets: ['latin', 'latin-ext'],
})

export async function generateStaticParams() {
return languages.map((lng) => ({ lng }))
}

type RootLayoutProps = {
children: React.ReactNode,
params: {
lng: string
}
}

async function getSession(cookie: string): Promise<Session | null> {
const response = await fetch(`${process.env.NEXTAUTH_URL}/api/auth/session`, {
headers: { cookie },
})

if (!response?.ok) {
return null
}

const session = await response.json()
return Object.keys(session).length > 0 ? session : null
}

export default async function RootLayout({ children, params }: RootLayoutProps) {
const session = await getSession('next-auth.session-token')
const { i18n } = await useTranslation(params.lng)
console.log(i18n.dir(), params.lng)
return (
<html lang={i18n.language} dir={i18n.dir()}>
<head />
<body className={dm_sans.variable}>
<AuthProvider session={session}>
{children}
</AuthProvider>
</body>
</html>
)
}
Loading

0 comments on commit 205843c

Please sign in to comment.