이번에 회사에서 서비스 외에 새로운 프로젝트를 진행하게 되었는데
레포부터 새로 생성하는 거라 새로운, 안써본, 트렌디한 스택으로 진행하기로 했다
그 중 next14
로 시작하면서, 결제, 다국어 지원을 내가 맡아 적용하기로 했다
결제는 경험이 있어 크게 어렵지 않았는데,
다국어 지원은 생각보다 레퍼런스가 별로 없어 공식문서에 의지하며 적용한 내용을 기록해본다!!
(진짜.. 3일만에 만들어서 QA 하고 운영배포 하고..😩 그 다음주인 2주차에 글로벌 작업 2일만에 하고.. QA 준비를 하고 있다..😫 빡신 일정이었지만.. 계획대로 진행되어 다행.. 과정에 많은 빡침🤬이 있었지만.. 뭐.. 그래..)
├── locales
│ ├── client.ts // 클라이언트에서 동작하도록 셋팅
│ ├── server.ts // 서버에서 동작하도록 셋팅
│ ├── en.ts
│ └── ko.ts
├── middleware.ts // defaultLocale, locales 설정 등
└── app
└── [locale]
├── layout.tsx
└── page.tsx
middleware.ts
import { createI18nMiddleware } from 'next-international/middleware';
import type { NextRequest } from 'next/server';
const I18nMiddleware = createI18nMiddleware({
locales: ['ko', 'en'],
defaultLocale: 'ko',
});
export function middleware(request: NextRequest) {
return I18nMiddleware(request);
}
export const config = {
matcher: ['/((?!api|static|.*\\..*|_next|favicon.ico|robots.txt).*)'],
};
locales/server.ts
import { createI18nServer } from 'next-international/server';
export const { getI18n, getScopedI18n, getCurrentLocale, getStaticParams } =
createI18nServer(
{
ko: () => import('./ko'),
en: () => import('./en'),
},
{
// Uncomment to use custom segment name
// segmentName: 'locale',
// Uncomment to set fallback locale
// fallbackLocale: en,
},
);
locales/client.ts
import { createI18nClient } from 'next-international/client';
export const {
useI18n,
useScopedI18n,
I18nProviderClient,
useChangeLocale,
defineLocale,
useCurrentLocale,
} = createI18nClient(
{
ko: () => import('./ko'),
en: () => import('./en'),
},
{
// Uncomment to set base path
// basePath: '/base',
// Uncomment to use custom segment name
// segmentName: 'locale',
// Uncomment to set fallback locale
// fallbackLocale: en,
},
);
locales/ko.ts
console.log('Loaded KO');
export default {
hello: '안녕',
welcome: '안녕 {name}!',
'detail.title': '헬로우봇 AI 프로필 생성기',
} as const;
locales/en.ts
console.log('Loaded EN');
export default {
hello: 'Hello',
welcome: 'Hello {name}!',
'detail.title': 'hello ai profile',
} as const;
app/[locale]/provider.tsx
'use client';
import type { ReactNode } from 'react';
import { I18nProviderClient } from '../../locales/client';
type ProviderProps = {
locale: string;
children: ReactNode;
};
export function Provider({ locale, children }: ProviderProps) {
return (
<I18nProviderClient locale={locale} fallback={<p>Loading...</p>}>
{children}
</I18nProviderClient>
);
}
import { getI18n, getScopedI18n, getCurrentLocale } from '../../../../locales/server';
import { useChangeLocale, useCurrentLocale } from '../../../../locales/client'; // 'use client'; 에서 적용
import { Provider } from '@/app/[locale]/provider.ts';
export default function MyComponent () {
const t = await getI18n(); // 단일 사용
const t2 = await getScopedI18n('detail'); // 묶음으로 사용
const currentLocale = getCurrentLocale(); // 현재 값 가져오기
const locale = useCurrentLocale() // 현재 값 가져오기, 'use client'; 에서 적용
const changeLocale = useChangeLocale(); // 언어 변경하기
const onClick = () => {
// 'use client'; 에서 적용
changeLocale('en');
};
return (
<>
<h1>{t('title')}</h1>
<p>{t2('title')}</p> {/* detail.title 값이 적용 됨 */}
<p>{t('welcome', { name: 'yurim' })}</p> {/* 변수 사용 가능 */}
<button type="button" onClick={onClick}>언어 변경</button>
<Provider locale={locale}> {/* 가장 하위의 컴포넌트에 적용할 것! */}
<ClientComponent /> {/* client component인 경우는 Provider로 감싸야 적용 됨 */}
</Provider>
</>
);
}
포스팅 잘 봤습니다.
첫 문단에서 깊은 분노가 느껴졌지만... ㅎ
덕분에 next-international 을 알게되었네요 :)