feat: add i18n settings
This commit is contained in:
parent
0043851e5c
commit
2e5346a387
@ -17,6 +17,7 @@
|
||||
"mathjs": "^13.0.2",
|
||||
"mongodb": "^6.8.0",
|
||||
"next": "14.2.3",
|
||||
"next-international": "^1.2.4",
|
||||
"react": "^18",
|
||||
"react-datepicker": "^7.3.0",
|
||||
"react-dom": "^18",
|
||||
|
||||
11
src/app/[locale]/LocaleProvider.js
Normal file
11
src/app/[locale]/LocaleProvider.js
Normal file
@ -0,0 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import { I18nProviderClient } from '@/locales/client'
|
||||
|
||||
export function LocaleProvider({ locale, children }) {
|
||||
return (
|
||||
<I18nProviderClient locale={locale} fallback={<p>Loading...</p>}>
|
||||
{children}
|
||||
</I18nProviderClient>
|
||||
)
|
||||
}
|
||||
4
src/app/[locale]/changelog/changelog.module.css
Normal file
4
src/app/[locale]/changelog/changelog.module.css
Normal file
@ -0,0 +1,4 @@
|
||||
.test {
|
||||
@apply bg-red-500;
|
||||
@apply text-2xl;
|
||||
}
|
||||
73
src/app/[locale]/changelog/page.jsx
Normal file
73
src/app/[locale]/changelog/page.jsx
Normal file
@ -0,0 +1,73 @@
|
||||
'use client'
|
||||
|
||||
import { Button, Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@nextui-org/react'
|
||||
import Hero from '@/components/Hero'
|
||||
import QSelect from '@/components/ui/QSelect'
|
||||
import styles from './changelog.module.css'
|
||||
import { get } from '@/lib/Axios'
|
||||
|
||||
export default function changelogPage() {
|
||||
const testVar = process.env.NEXT_PUBLIC_TEST
|
||||
|
||||
const handleUsers = async () => {
|
||||
const users = await get('/api/user/find-all')
|
||||
console.log(users)
|
||||
}
|
||||
|
||||
const data = [
|
||||
{
|
||||
id: 1,
|
||||
author: 'SWYOO',
|
||||
contents: '버튼 정리(템플릿 적용)',
|
||||
date: '2024.07.16',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
author: 'SWYOO',
|
||||
contents: 'README.md 파일 이미지 경로 수정',
|
||||
date: '2024.07.17',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
author: 'SWYOO',
|
||||
contents: '',
|
||||
date: '',
|
||||
},
|
||||
]
|
||||
return (
|
||||
<>
|
||||
<Hero title="Change log" />
|
||||
<div className={styles.test}>이 영역은 테스트입니다.</div>
|
||||
<div>
|
||||
<Table isStriped>
|
||||
<TableHeader>
|
||||
<TableColumn>DATE</TableColumn>
|
||||
<TableColumn>NAME</TableColumn>
|
||||
<TableColumn>CONTENTS</TableColumn>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{data.map((item) => (
|
||||
<TableRow key={item.id}>
|
||||
<TableCell>{item.date}</TableCell>
|
||||
<TableCell>{item.author}</TableCell>
|
||||
<TableCell>{item.contents}</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="m-2">
|
||||
<QSelect />
|
||||
</div>
|
||||
<div className="w-full bg-orange-300 m-2">{testVar}</div>
|
||||
<div>
|
||||
<div className="m-2">
|
||||
<Button onClick={handleUsers}>Button</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="test">
|
||||
<p className="text-white">Sass 테스트입니다.</p>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
15
src/app/[locale]/error.jsx
Normal file
15
src/app/[locale]/error.jsx
Normal file
@ -0,0 +1,15 @@
|
||||
'use client'
|
||||
|
||||
export default function ServerError() {
|
||||
return (
|
||||
<section className="bg-white dark:bg-gray-900">
|
||||
<div className="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
|
||||
<div className="mx-auto max-w-screen-sm text-center">
|
||||
<h1 className="mb-4 text-7xl tracking-tight font-extrabold lg:text-9xl text-primary-600 dark:text-primary-500">500</h1>
|
||||
<p className="mb-4 text-3xl tracking-tight font-bold text-gray-900 md:text-4xl dark:text-white">Internal Server Error.</p>
|
||||
<p className="mb-4 text-lg font-light text-gray-500 dark:text-gray-400">We are already working to solve the problem. </p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@ -6,7 +6,6 @@ import Intro from '@/components/Intro'
|
||||
export default function IntroPage() {
|
||||
return (
|
||||
<>
|
||||
<Hero title="Common Components Intro Page" />
|
||||
<div className="container mx-auto p-4 m-4 border">
|
||||
<Intro />
|
||||
</div>
|
||||
13
src/app/[locale]/layout.js
Normal file
13
src/app/[locale]/layout.js
Normal file
@ -0,0 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import { useCurrentLocale } from '@/locales/client'
|
||||
import { LocaleProvider } from './LocaleProvider'
|
||||
|
||||
export default function LocaleLayout({ children }) {
|
||||
const locale = useCurrentLocale()
|
||||
return (
|
||||
<>
|
||||
<LocaleProvider locale={locale}>{children}</LocaleProvider>
|
||||
</>
|
||||
)
|
||||
}
|
||||
25
src/app/[locale]/not-found.jsx
Normal file
25
src/app/[locale]/not-found.jsx
Normal file
@ -0,0 +1,25 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<section className="bg-white dark:bg-gray-900">
|
||||
<div className="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
|
||||
<div className="mx-auto max-w-screen-sm text-center">
|
||||
<h1 className="mb-4 text-7xl tracking-tight font-extrabold lg:text-9xl text-primary-600 dark:text-primary-500">404</h1>
|
||||
<p className="mb-4 text-3xl tracking-tight font-bold text-gray-900 md:text-4xl dark:text-white">Something's missing.</p>
|
||||
<p className="mb-4 text-lg font-light text-gray-500 dark:text-gray-400">
|
||||
Sorry, we can't find that page. You'll find lots to explore on the home page.{' '}
|
||||
</p>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex text-white bg-primary-600 hover:bg-primary-800 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:focus:ring-primary-900 my-4"
|
||||
>
|
||||
Back to Homepage
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
19
src/app/[locale]/page.js
Normal file
19
src/app/[locale]/page.js
Normal file
@ -0,0 +1,19 @@
|
||||
import MainPage from '@/components/Main'
|
||||
import { getCurrentLocale } from '@/locales/server'
|
||||
|
||||
export default function page() {
|
||||
const currentLocale = getCurrentLocale()
|
||||
|
||||
const mainPageProps = {
|
||||
currentLocale,
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="m-4">
|
||||
<h3>Main</h3>
|
||||
<MainPage {...mainPageProps} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -2,7 +2,7 @@ import Link from 'next/link'
|
||||
|
||||
export default function Headers() {
|
||||
return (
|
||||
<div className="w-full absolute z-10">
|
||||
<div className="w-full">
|
||||
<nav className="container relative flex flex-wrap items-center justify-between mx-auto p-8">
|
||||
<Link href="/" className="font-bold text-3xl">
|
||||
Home
|
||||
|
||||
@ -1,5 +1,27 @@
|
||||
import Hero from './Hero'
|
||||
'use client'
|
||||
|
||||
export default function Main() {
|
||||
return <Hero title="Q.CAST III - Prototype" />
|
||||
import { useChangeLocale, useI18n } from '@/locales/client'
|
||||
import { Button, Chip } from '@nextui-org/react'
|
||||
|
||||
export default function MainPage(props) {
|
||||
const { currentLocale } = props
|
||||
const t = useI18n()
|
||||
const changeLocale = useChangeLocale()
|
||||
|
||||
const handleChangeLocale = () => {
|
||||
currentLocale === 'ja' ? changeLocale('ko') : changeLocale('ja')
|
||||
}
|
||||
|
||||
console.log('MainPage', currentLocale)
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3>{t('locale', { locale: <Chip color="primary">{currentLocale}</Chip> })}</h3>
|
||||
<div>{t('hello')}</div>
|
||||
<div>{t('welcome', { name: '효준' })}</div>
|
||||
<div>
|
||||
<Button onClick={handleChangeLocale}>Change Locale</Button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
18
src/locales/client.js
Normal file
18
src/locales/client.js
Normal file
@ -0,0 +1,18 @@
|
||||
'use client'
|
||||
|
||||
import { createI18nClient } from 'next-international/client'
|
||||
|
||||
export const { useI18n, useScopedI18n, I18nProviderClient, useChangeLocale, defineLocale, useCurrentLocale } = createI18nClient(
|
||||
{
|
||||
ko: () => import('./ko'),
|
||||
ja: () => import('./ja'),
|
||||
},
|
||||
{
|
||||
// Uncomment to set base path
|
||||
// basePath: '/base',
|
||||
// Uncomment to use custom segment name
|
||||
// segmentName: 'locale',
|
||||
// Uncomment to set fallback locale
|
||||
// fallbackLocale: en,
|
||||
},
|
||||
)
|
||||
7
src/locales/ja.js
Normal file
7
src/locales/ja.js
Normal file
@ -0,0 +1,7 @@
|
||||
console.log('Loaded JA')
|
||||
|
||||
export default {
|
||||
hello: 'こんにちは',
|
||||
welcome: 'こんにちは {name}!',
|
||||
locale: '現在のロケールは {locale} です。',
|
||||
}
|
||||
7
src/locales/ko.js
Normal file
7
src/locales/ko.js
Normal file
@ -0,0 +1,7 @@
|
||||
console.log('Loaded KO')
|
||||
|
||||
export default {
|
||||
hello: '안녕',
|
||||
welcome: '안녕 {name}!',
|
||||
locale: '현재 로케일은 {locale}입니다.',
|
||||
}
|
||||
14
src/locales/server.js
Normal file
14
src/locales/server.js
Normal file
@ -0,0 +1,14 @@
|
||||
import { createI18nServer } from 'next-international/server'
|
||||
|
||||
export const { getI18n, getScopedI18n, getCurrentLocale, getStaticParams } = createI18nServer(
|
||||
{
|
||||
ko: () => import('./ko'),
|
||||
ja: () => import('./ja'),
|
||||
},
|
||||
{
|
||||
// Uncomment to use custom segment name
|
||||
// segmentName: 'locale',
|
||||
// Uncomment to set fallback locale
|
||||
// fallbackLocale: en,
|
||||
},
|
||||
)
|
||||
@ -1,12 +1,27 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { createI18nMiddleware } from 'next-international/middleware'
|
||||
|
||||
const I18nMiddleware = createI18nMiddleware({
|
||||
locales: ['ko', 'ja'],
|
||||
defaultLocale: 'ko',
|
||||
})
|
||||
|
||||
export function middleware(request) {
|
||||
const requestHeaders = new Headers(request.headers)
|
||||
requestHeaders.set('x-pathname', request.nextUrl.pathname)
|
||||
|
||||
return NextResponse.next({
|
||||
request: {
|
||||
headers: requestHeaders,
|
||||
},
|
||||
})
|
||||
return I18nMiddleware(request)
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: ['/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)'],
|
||||
}
|
||||
|
||||
// import { NextRequest, NextResponse } from 'next/server'
|
||||
|
||||
// export function middleware(request) {
|
||||
// const requestHeaders = new Headers(request.headers)
|
||||
// requestHeaders.set('x-pathname', request.nextUrl.pathname)
|
||||
|
||||
// return NextResponse.next({
|
||||
// request: {
|
||||
// headers: requestHeaders,
|
||||
// },
|
||||
// })
|
||||
// }
|
||||
|
||||
21
yarn.lock
21
yarn.lock
@ -2314,7 +2314,7 @@ chownr@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
|
||||
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
|
||||
|
||||
client-only@0.0.1:
|
||||
client-only@0.0.1, client-only@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz"
|
||||
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
|
||||
@ -2773,6 +2773,11 @@ inherits@2, inherits@^2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
international-types@^0.8.1:
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/international-types/-/international-types-0.8.1.tgz#c0e593d9911c1a23f64bbd6eb1abb2941fe2353f"
|
||||
integrity sha512-tajBCAHo4I0LIFlmQ9ZWfjMWVyRffzuvfbXCd6ssFt5u1Zw15DN0UBpVTItXdNa1ls+cpQt3Yw8+TxsfGF8JcA==
|
||||
|
||||
intl-messageformat@^10.1.0:
|
||||
version "10.5.14"
|
||||
resolved "https://registry.yarnpkg.com/intl-messageformat/-/intl-messageformat-10.5.14.tgz#e5bb373f8a37b88fbe647d7b941f3ab2a37ed00a"
|
||||
@ -3097,6 +3102,15 @@ nanoid@^3.3.6, nanoid@^3.3.7:
|
||||
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz"
|
||||
integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
|
||||
|
||||
next-international@^1.2.4:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/next-international/-/next-international-1.2.4.tgz#abe50b2aa3ba7ecf92d41f87537796a4b2dd0ba3"
|
||||
integrity sha512-JQvp+h2iSgA/t8hu5S/Lwow1ZErJutQRdpnplxjv4VTlCiND8T95fYih8BjkHcVhQbtM+Wu9Mb1CM32wD9hlWQ==
|
||||
dependencies:
|
||||
client-only "^0.0.1"
|
||||
international-types "^0.8.1"
|
||||
server-only "^0.0.1"
|
||||
|
||||
next@14.2.3:
|
||||
version "14.2.3"
|
||||
resolved "https://registry.npmjs.org/next/-/next-14.2.3.tgz"
|
||||
@ -3523,6 +3537,11 @@ semver@^7.3.5:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
|
||||
integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
|
||||
|
||||
server-only@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/server-only/-/server-only-0.0.1.tgz#0f366bb6afb618c37c9255a314535dc412cd1c9e"
|
||||
integrity sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==
|
||||
|
||||
set-blocking@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user