[ 살펴보기 ] Next-Intl - Basics
![[ 살펴보기 ] Next-Intl - Basics](https://cdn.hashnode.com/res/hashnode/image/upload/v1734165108180/67d8ac04-15c0-43b9-a599-2efa4e72992c.jpeg)
Next-Intl은 next application에서 다국어 기능을 제공할 때 사용할 수 있는 library다. Page title, subtitle등과 같이 page에 사용되는 text를 각 언어별 json 파일로 구분해두고 유저가 선택한 언어에 따라 다른 json 파일을 적용에 선택한 언어 맞는 content를 제공할 수 있다.
해당 포스트는 NextJS App route를 통해 next-intl을 사용하는 방법을 살펴본다. Next-intl을 통해 다국어 기능을 적용하는 방법은 두 가지다. example.com/en/about과 같이 langauge정보가 pathname에 포함하는 방식이 있고 language 정보가 pathname에 포함하지 않는 방법이 있다. 해당 포스트에선 language 정보가 pathname에 포함되는 방식을 살펴본다.
우선 테스트를 진행하기 위해 next-intl package를 설치한다.
npm install next-intl
With I18n Routing
테스트를 위한 project 구조는 다음과 같다.
...
next.config.ts
/src
- middlewarets
/app
[locale]
- layout.tsx
- page.tsx
/user
- page.tsx
/i18n
- routing.ts
- request.ts
/messages
- en.json
- ko.json
/messages경로에 각 언어마다 적용될 content 내용을 추가한다. 아래 예제에선 user page에서 사용될 title, about 영어 정보는 en.json 파일에 title, about 한글 정보는 ko.json 파일에서 관리되고 있다.
messages/en.json
{
"userPageInfo": {
"title": "User page",
"about": "This is an user page"
}
}
messages/ko.json
{
"userPageInfo": {
"title": "유저 페이지",
"about": "유저 페이지 소개"
}
}
그리고 next.config.ts 파일을 다음과 같이 설정해준다.
next.config.ts
import createNextIntlPlugin from 'next-intl/plugin';
const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */
const nextConfig = {};
export default withNextIntl(nextConfig);
Next-Intl을 통해 translation 기능을 추가할 때 internationalization 관련 설정은 next.config.ts에 바로 추가 하지 않고 다음과 같이 별도의 경로에 따로 추가하여 사용한다.
src/i18n/routing.ts
import {defineRouting} from 'next-intl/routing';
import {createNavigation} from 'next-intl/navigation';
export const routing = defineRouting({
locales: ['en', 'ko'],
defaultLocale: 'en'
});
export const {Link, redirect, usePathname, useRouter, getPathname} =
createNavigation(routing);
그리고 위에서 추가한 configuration을 middleware에 적용한다.
src/middleware.ts
import createMiddleware from 'next-intl/middleware';
import {routing} from './i18n/routing';
export default createMiddleware(routing);
export const config = {
matcher: ['/', '/(ko|en)/:path*']
};
이제 src/i18n/request.ts파일을 통해 request pathname에 포함된 language 정보에 따라 어떤 language translation 파일을 사용해야 하는지 설정한다.
src/i18n/request.ts
import {getRequestConfig} from 'next-intl/server';
import {routing} from './routing';
export default getRequestConfig(async ({requestLocale}) => {
let locale = await requestLocale;
if (!locale || !routing.locales.includes(locale as any)) {
locale = routing.defaultLocale;
}
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default
};
});
위의 설정에서 현재 request pathname에 포함된 language가 src/i18nrouting.ts에서 추가한 language option에 포함되어 있는 language인지 검사하고 만약 src/i18n/routing.ts 설정에서 추가한 language option이 아닌 다른 language가 포함되어 있다면 default로 설정한 langauge를 사용한다.
예를 들어 우리는 위에서 English ( en )와 Korean ( kr ) 두 가지 언어를 routing.ts 설정 파일에 추가하고 default 언어는 en로 설정했으므로 example.com/fr/user와 같이 routing.ts 파일에 추가하지 않은 language가 request pathname에 포함되어 있으면 default 언어인 en를 사용한다.
이제 next application의 각 page나 component에서 현재 request pathname에 포함된 langauge에 따라 /messages 경로에 추가한 각 translation파일 내용을 사용할 수 있도록 layout.tsx에 다음과 같이 설정해준다.
src/app/[locale]/layout.tsx
import "./globals.css";
import type { Metadata } from "next";
import { NextIntlClientProvider } from "next-intl";
import { getMessages } from "next-intl/server";
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const messages = await getMessages();
return (
<html>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
위의 예제에서 볼 수 있듯이 layout의 children을 NextIntlClientProvier로 감싸고 provider에 message property를 전달하고 있다. getMessage function은 현재 request의 pathname에 포함된 language에 따른 translation json 파일을 object 형태로 return해준다.
만약 example.com/en/user와 같이 pathname에 포함된 language가 english면 en.json 내용을 object로 return하고 example.com/ko/user와 같이 pathname에 포함된 language가 korean이면 ko.json 내용을 object로 return한다.
이제 page나 component에서 translation 파일이 제공하는 내용을 사용할 준비가 기본적인 설정이 끝났다. 이제 user page에 translation 파일의 내용을 적용해보자.
src/app/[locale]/user/page.tsx
import { useTranslations } from "next-intl";
import React from "react";
const UserPage = () => {
const t = useTranslations("userPageInfo");
return (
<div>
<h3>User</h3>
<div>{t("title")}</div>
<div>{t("about")}</div>
</div>
);
};
export default UserPage;
이제 위의 페이지를 example.com/en/user와 example.com/ko/user와 같이 pathname에 language를 변경하며 접근해보면 각 언어별 json file에 추가한 값이 pathname에 포함된 langauge에 맞게 적용되는 것을 확인할 수 있다.
Static render
위의 예제와 같이 routing을 통해 다국어 기능을 제공하면 모든 page가 dynamic route에 포함 되므로 build시 모든 page가 default로 dynamic render로 취급된다. Application Build시 [locale] dynamic route 아래 각 page를 다시 static render로 취급하기 위해선 다음과 같은 추가 작업이 필요하다.
만약 [locale] dynamic route 하위 모든 page에 static render를 적용하고자 한다면 application 최상단 layout 파일에 다음과 같이 generateStaticParams와 setRequestLocale를 추가 해준다.
src/layout.tsx
import { routing } from "@/i18n/routing";
import "./globals.css";
import type { Metadata } from "next";
import { getMessages, setRequestLocale } from "next-intl/server";
import { NextIntlClientProvider } from "next-intl";
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export function generateStaticParams() {
return routing.locales.map((locale) => ({ locale }));
}
export default async function RootLayout({
children,
params,
}: Readonly<{
children: React.ReactNode;
params: Promise<{ locale: string }>;
}>) {
const locale = (await params).locale;
setRequestLocale(locale);
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
위의 예제와 같이 layout의 params parameter를 통해 dynamic route의 parameter를 조회할 수 있다. 우리의 경우에는 dynamic route를 [locale] 형식으로 사용하고 있으므로 params은 param: { locale: string } 구조를 갖는다. 또한 dynamic route의 param을 조회하는 방법은 NextJS의 version에 따라 조금씩 차이가 있으므로 주의하자. 위의 예제는 15.1.0 version을 기준으로 작성되었다. ( Reference - Dynamic route )
layout에 generateStaticParams와 setRequestLocale를 추가하고 application을 build 해보면 [locale] dynamic route 하위 page가 다시 static render가 되는 것을 확인할 수 있다.
주의할 점은 위의 변경 사항으로 인해 모든 page가 static render가 되는 것은 아니다. NextJS가 제공하는 cookie나 header와 같은 function을 사용하는 경우와 같이 원래 dynamic render로 취급하는 경우는 그대로 dynamic render가 적용된다.
![[ 살펴보기 ] RDB - Relationships](https://cdn.hashnode.com/res/hashnode/image/upload/v1739711556668/48dc9e84-a621-42aa-9c9f-5fc5c436f0ec.jpeg)
![[ 살펴보기 ] MySQL - Data types](https://cdn.hashnode.com/res/hashnode/image/upload/v1739593589113/530f8704-4d27-42c9-a451-bb5c63150b99.jpeg)
![[ 살펴보기 ] TypeORM - Transactions, Migration](https://cdn.hashnode.com/res/hashnode/image/upload/v1739106042581/980b8133-61d4-406a-a026-65be9c28eace.jpeg)
![[ 살펴보기 ] TypeORM - Relations](https://cdn.hashnode.com/res/hashnode/image/upload/v1738666874402/b688bd0b-b6bb-4f43-87d8-c1b46b59f1b7.jpeg)
![[ 살펴보기 ] TypeORM - Basics](https://cdn.hashnode.com/res/hashnode/image/upload/v1738666803591/bef5df17-7dc7-4123-ae55-004d5042df39.jpeg)