다국어 설정 변경작업
개요
다국적 기업의 웹페이지는 여러 언어로 되어 있다. 한개의 언어를 고정으로 단순하게 string을 넣고 있다면 다른 언어를 지원할 때 같은 페이지를 또 만들어야하는 문제가 발생한다. 이 문제를 해결하기 위해 다국어 지원을 위한 라이브러리가 있다.
- i18n : 일반 react 프로젝트
- next-intl : nextjs 프로젝트
위와같은 라이브러리로 지원된다. 위 라이브러리의 중 Nextjs 의 다국어 프로젝트 설정을 알아보자
코드
next.config.js
다국어 모듈을 적용하기 해서 플러그인을 선 적용해준다.
const createNextIntlPlugin = require('next-intl/plugin');
const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */
const nextConfig = {
// 기존 설정들 생략...
};
module.exports = withNextIntl(nextConfig);
src/i18n/request.ts
어떤 설정을 가져올지 정할 수 있다. cookie 및 header에서 설정이 가능하다,
가장 간단한 방법은 경로로 한번 감싸는 것이다. origin/ko,/~ origin/en/~ 같이 변경하면 좋지만 내 블로그 글들은 어차피 다 한글이기 때문에 연습삼아 적용한 것이고 url까지 나누진 않도록 하겠다.
import {getRequestConfig} from 'next-intl/server';
import {hasLocale} from 'next-intl';
import {routing} from './routing';
// messages import (정적 import)
import ko_main_menu from './messages/ko/main-menu.json';
import en_main_menu from './messages/en/main-menu.json';
import ko_note_edit from './messages/ko/note-edit.json';
import en_note_edit from './messages/en/note-edit.json';
// messages 객체로 통합
const messages: Record<string, Record<string, any>> = {
ko: {
main_menu: ko_main_menu,
note_edit: ko_note_edit,
},
en: {
main_menu: en_main_menu,
note_edit: en_note_edit,
},
};
export default getRequestConfig(async (param) => {
// Typically corresponds to the `[locale]` segment
const requested = await param.requestLocale;
const locale = hasLocale(routing.locales, requested)
? requested
: routing.defaultLocale;
return {
locale,
messages: messages[locale] || messages[routing.defaultLocale],
};
});
src/i18n/navigation.ts
Link나 redirect 같은 부분에서 locale 을 사용해서 움직일 수 있도록 하기 위해서 next-intl에서 제공하는 Link, redirect를 사용한다.
import {createNavigation} from 'next-intl/navigation';
import {routing} from './routing';
// Lightweight wrappers around Next.js' navigation
// APIs that consider the routing configuration
export const {Link, redirect, usePathname, useRouter, getPathname} =
createNavigation(routing);
실사용 component
import { useTranslations } from 'next-intl';
import { Link } from '@/i18n/navigation';
import { MenuItem } from '@/types/menu';
import { DrawerClose } from './ui/drawer';
export function SidebarMenu({ menu }: { menu: MenuItem[] }) {
const t = useTranslations('main_menu');
return (
<ul className="flex flex-col gap-2 p-4">
{menu.map((item) => (
<li key={item.href}>
<DrawerClose asChild>
<Link
href={item.href}
className="block rounded px-3 py-2 hover:bg-gray-200 dark:hover:bg-gray-700"
>
{item.icon}
{t(item.labelKey)}
</Link>
</DrawerClose>
{item.children && item.children.length > 0 && (
<SidebarMenu menu={item.children} />
)}
</li>
))}
</ul>
);
}
언어저장용 messages/ko/main-menu.json
{
"title": "메인 메뉴",
"dashboard": "대시보드",
"blog": "블로그",
"game": "게임",
"note": "노트",
"rag": "RAG",
"about": "소개",
"blogResources": "코딩자료",
"blogTest": "test",
"boardgame": "보드게임",
"mincraft": "미니크래프트",
"services": "서비스",
"contact": "연락처",
"sign-in": "로그인",
"sign-up": "회원가입"
}
참고사항
1. next-intl의 Link asChild문제
next-intl의 Link는 serverComponent에서 사용시 asChild가 적용되지 않는 문제가 있을 수 있다.
// 작동안함.
import { Link } from '@/i18n/navigation';
<Button asChild>
<Link href="/">
{t('sign-in')}
</Link>
</Button>
- scadn ui 에서는 다음과 같이 button class 에 스타일을 적용해서 같은 스타일을 적용할 수 있다.
<Link
href="/sign-up"
className={buttonVariants({ variant: 'default', size: 'sm' })}>
{t('sign-up')}
</Link>