최근 글로벌 시장 진출을 고려하는 서비스가 많아지면서, 웹 애플리케이션에 국제화(i18n)와 지역화(l10n)를 적용하는 일이 중요해졌습니다. 특히 Next.js는 최신 프레임워크로 많은 개발자들의 사랑을 받고 있지만, 국제화를 구현하는 데 있어서 다양한 옵션과 접근 방식이 있어 개발자들에게 고민을 안겨줄 수 있습니다. 이 글에서는 Next.js를 활용해 국제화를 구현한 실제 경험을 공유하고, 이를 통해 얻은 교훈과 실용적인 팁을 제공하려고 합니다.
1. 배경
그 동안 한국 내에서만 서비스할 제품을 개발했기 때문에 한국어 이외의 언어를 표시할 일이 없었습니다. 그래서 UI나 콘텐츠에 포함되는 텍스트는 한글로 하드코딩하는 등, 제품을 개발하는 과정에서 한국어 이외의 언어를 제공하는 상황을 고려하지 않았습니다. 그런데 최근 회사에서 Next.js로 개발 중인 서비스를 미국에 수출하기 위해, 서비스에 영어로 번역된 콘텐츠를 추가하는 일을 진행하게 되었습니다. 또한 장기적으로 다른 국가로 수출 가능성을 염두에 두고 영어 뿐만 아니라 다른 언어를 지원하는 방안을 고민해야 했습니다.
이러한 문제를 해결하기 위해 Next.js에서 다양한 언어 콘텐츠를 제공하기 위한 효율적인 방법을 조사하였습니다. 그리고 조사하는 과정에서 저희가 해결하고자 하는 문제는 국제화(i18n)와 현지화(l10n)라는 개념과 관련되어 있다는 것을 알게 되었습니다.
2. 국제화와 지역화
예를 들어 쇼핑몰 웹사이트를 개발한다고 가정해봅시다. 웹사이트에서 장바구니에 물건을 담을 때, 한국어 사용자에게는 ‘장바구니에 담기’ 버튼을, 영어 사용자에게는 ‘Add to Cart’라는 버튼을 보여줘야 합니다. 또한 제품에 표시될 화폐 단위나 날짜 형식 등도 사용자의 언어와 지역 문화에 맞게 표시해야 합니다. 이러한 기능을 효율적으로 구현하는 것이 국제화와 지역화입니다.
국제화(Internationalization, i18n)
국제화는 제품이 다양한 언어, 문자, 날짜 형식, 숫자 양식 등을 지원할 수 있도록 설계하고 개발하는 과정입니다. 국제화는 흔히 i18n으로 줄여 부르는데, 영어로 국제화를 뜻하는 internationalization이라는 단어의 i와 n 사이에 18개의 문자가 있기 때문입니다.
국제화의 대표적인 예로는 유니코드의 사용이 있습니다. 유니코드는 거의 모든 언어와 문자를 지원하며, 다국어 환경에서 텍스트를 손쉽게 처리할 수 있는 표준으로 자리 잡았습니다. 또한, 국제화된 시스템은 지역별로 서로 다른 날짜와 숫자 형식을 자동으로 조정합니다. 예를 들어, 한국에서는 날짜를 ‘2025.01.02’와 같은 형식으로 표시하지만, 미국에서는 ‘01/02/2025’로 표현합니다. 마지막으로, 오른쪽에서 왼쪽으로 쓰는 언어인 아랍어나 히브리어를 지원하기 위해 텍스트 방향을 조정하는 기능도 국제화의 한 예입니다.
국제화는 글로벌 시장에서 제품의 경쟁력을 높이는 데 필수적입니다. 제품 개발 초기에 국제화를 고려하면, 이후 새로운 국가로 확장할 때 드는 개발 비용과 시간을 대폭 절감할 수 있습니다. 또한, 사용자가 자신의 언어와 문화에 맞는 콘텐츠를 경험할 수 있도록 제공함으로써 사용자 만족도를 높이고 충성도를 강화할 수 있습니다. 결과적으로, 국제화는 제품을 세계적인 수준으로 끌어올리는 중요한 역할을 합니다.
현지화(Localization, l10n)
현지화 또는 지역화는 국제화를 통해 마련된 기반 위에서 특정 지역이나 문화에 맞는 콘텐츠를 실제로 구현하고 제공하는 과정입니다. 현지화는 l10n으로도 불리며, 영어로 현지화를 뜻하는 localization의 l과 n 사이에 10개의 문자가 있는 것에서 유래했습니다.
현지화의 가장 흔한 예는 언어 번역입니다. 예를 들어, 영어로 된 “Add to Cart” 버튼을 한국어로 “장바구니에 담기”로 번역하는 작업이 현지화의 한 부분입니다. 또한, 지역적 이미지와 색상 사용도 중요한데, 특정 문화에서 긍정적으로 받아들여지는 색상이나 이미지를 콘텐츠에 반영함으로써 사용자 친화성을 높일 수 있습니다. 예를 들어, 중국에서는 빨간색이 행운의 상징으로 여겨지지만, 서양에서는 경고를 나타낼 수 있습니다. 마지막으로, 화폐와 가격 형식을 지역에 맞게 조정하는 작업도 지역화의 한 예입니다. 한국에서는 원화(₩)를 사용하지만, 미국에서는 달러($)를 사용하며, 소수점과 천 단위 구분도 각 지역에 맞게 변환해야 합니다.
요약
- 국제화(i18n): 글로벌 시장을 지원할 수 있도록 제품을 설계하고 개발하는 준비 과정. (예: 유니코드 지원, 날짜 및 텍스트 방향 처리)
- 지역화(l10n): 특정 언어 및 문화에 맞는 콘텐츠를 번역하고 최적화하는 구현 작업. (예: 텍스트 번역, 화폐 및 날짜 형식 변환)
3. Next.js를 위한 국제화 라이브러리 선택
Next.js에서는 기본적으로 다양한 언어를 지원하기 위한 언어별 경로를 설정할 수 있습니다. 최상위 경로 세그먼트를
locale
이라는 Dynamic route로 지정하여 언어별 콘텐츠를 제공하거나, middleware에서 경로상의 locale
을 체크하여 언어에 맞는 리디렉션 기능을 추가할 수 있습니다. 또한 언어별로 JSON으로 저장된 텍스트를 관리하고 불러올 수도 있었습니다.하지만 이러한 기능을 직접 처리하기에는 번거로웠고, 라이브러리를 통해 보다 효율적으로 관리하는 것이 적합하다고 판단했습니다. 그래서 Next.js 공식 문서에서 소개된 몇 가지 국제화 라이브러리를 비교 검토했습니다. 다음은 라이브러리를 선택할 때 고려했던 기준입니다:
- 다양한 언어로 된 콘텐츠를 쉽게 관리할 수 있어야 한다.
- 사용법이 간단하고 문서가 잘 정리되어 있어야 한다.
- 활발하게 유지보수되고 있으며 사용자가 많아야 한다.
- 최신 Next.js와 잘 호환되어야 한다.
라이브러리 비교
공통적으로 모든 국제화 라이브러리들이 언어별 메시지를 json으로 관리하고 컴포넌트에서 쉽게 가져올 수 있는 기능을 제공하고 있으며, 아래 표와 같은 차이를 보였습니다.
라이브러리 | 주요 특징 | 장점 | 단점 |
next-i18next | 오래된 국제화 라이브러리로 검증된 솔루션 | 널리 사용되고, 학습 자료가 풍부함 | 설정이 복잡하고, 경로 탐색 지원이 부족함 |
next-international | 간단한 코드 중심의 다국어 관리 | 가볍고 타입스크립트 친화적 | 문서와 커뮤니티가 부족하고, 편의 기능이 제한적 |
Lingui | 플러럴 처리, 포맷팅 등 다양한 고급 기능 제공 | 복잡한 국제화 규칙에 적합 | 설정이 복잡하고, 사용법이 직관적이지 않음 |
paraglide-next | inlang 플랫폼과 통합하여 번역 데이터를 관리 | inlang의 번역 관리 기능과 연동 가능 | 최소한의 Next.js 지원 |
next-intl | 최신 Next.js와 완벽히 호환되는 가볍고 직관적인 국제화 라이브러리 | 잘 정리된 문서와 쉬운 사용법, 높은 Next.js 호환성 | 제한적인 json 기반 언어별 메시지 관리 |
국제화 라이브러리 선택: next-intl
다양한 라이브러리 중에서
next-intl
을 선택한 이유는 다음과 같습니다.- Next.js 호환성: 최신 Next.js의 서버 컴포넌트와 완벽히 호환되며, 클라이언트와 서버 양쪽을 모두 지원합니다.
- 편리한 사용성: 사용법이 간단하고 문서가 잘 정리되어 있어 학습 곡선이 낮습니다.
- 활발한 커뮤니티와 유지보수: Github에서 정기적인 업데이트가 이루어지고 있으며, npm trends에서도 높은 다운로드 수를 기록하고 있습니다.
- VSCode 확장 플러그인 지원: i18n-ally 또는 sherlock으로 번역 상태를 시각적으로 확인하거나, VS Code와 통합된 GUI로 번역 메시지를 쉽게 편집할 수 있습니다.
이러한 이유로,
next-intl
은 저희 프로젝트에서 사용하기 가장 적합한 라이브러리로 판단되었습니다.3. next-intl을 사용하여 해결한 Next.js 국제화 문제
locale
관리
MDN 문서(링크)에 의하면
locale
은 사용자 인터페이스에 대한 언어 또는 국가 기반 기본 설정 집합입니다. 다양한 국가에 맞는 콘텐츠를 제공할 때 이 locale
값으로 국가를 구분합니다. 따라서 locale
을 효율적으로 관리해야 합니다.next-intl에서는
locale
을 관리하는 두 가지 방식을 제공하고 있습니다.- Dynamic routes으로 최상위 경로 세그먼트에 locale을 추가하여 관리하기
- 예)
/[locale]/products
→/en/products
,/ko/products
- 도메인으로 locale을 관리하는 방식
- 예)
en.helloworld.com/products
,ko.helloworld.com/product
저희 프로젝트에서는 도메인이 하나만 사용되었기 때문에 Dynamic routes 방식을 선택했습니다. 이 방식은,
- 단일 도메인 내에서 언어별 콘텐츠를 명확히 구분할 수 있어 SEO 관리가 용이합니다.
- Next.js의 Dynamic routes와 결합해 쉽게 구현할 수 있습니다.
- locale 변경 시 경로만 변경하면 되므로, 유지보수가 간단합니다.
도메인 기반 관리 방식은 다국적 브랜드처럼 국가별/언어별로 도메인이 별도로 운영되는 경우에 적합하지만, 저희 프로젝트에는 불필요했습니다.
그리고 Next.js의 개별 페이지에서 locale 값이 필요할 때는 next-intl에서 제공하는
useLocale
이라는 리액트 훅을 사용했습니다. 이 훅은 서버 컴포넌트도 지원하고 있어서 활용도가 매우 높습니다. 참고로 page.tsx
에서 컴포넌트의 props로 dynamicroute의 locale
을 받을 경우 next-intl 내부적으로 관리하는 locale
과 일치하지 않을 수 있으므로 useLocale
로 locale
을 받아서 사용하는 것을 추천합니다.경로 탐색 관리
locale
은 경로로 관리되기 때문에, locale
을 변경하려면 경로를 변경해야 합니다. 만약 경로를 잘못 설정한 경우 사용자가 경로를 탐색할 때 잘못된 locale
로 인해 다른 지역 또는 언어 콘텐츠를 제공하는 문제가 생길 수 있습니다. next-intl은 경로를 탐색할 때 locale
을 삽입 및 관리해야 하는 불편함을 해결하기 위해, Next.js의 라우팅 API를 래핑하여 locale을 관리하고 변경할 수 있는 기능을 제공합니다. 가장 자주 사용했던 API는 Link 컴포넌트와 useRouter 훅입니다.Link (component)
import {Link} from '@/i18n/routing'; // 사용자가 '/ko'의 하위 경로에 있다면, '/ko/about'으로 이동함 <Link href="/about">About</Link> // locale을 en으로 변경하여 '/en/about'으로 이동함 // (a 태그의 `hreflang`속성이 함께 설정됨) <Link href="/about" locale="en">Switch to English</Link>
useRouter (react hook)
'use client'; import {useRouter} from '@/i18n/routing'; const router = useRouter(); // 사용자가 '/ko'의 하위 경로에 있다면, '/ko/about'으로 이동함 router.push('/about'); // locale을 en으로 변경하여 '/en/about'으로 이동함 router.replace('/about', {locale: 'en'});
번역 메시지 관리
next-intl에서 권장하는 방법
next-intl에서 권장되는 번역 메시지 관리 방법은 언어별로 json 파일을 만들고, 각 json 파일에 프로젝트에서 사용되는 모든 번역 가능한 텍스트를 저장하는 것입니다.
- 이렇게 하면 파일을 하나 추가하는 것으로 언어를 추가할 수 있으므로 편리합니다.
useTranslations
이라는 훅을 사용하면 현재 선택된locale
과 대응되는 json 파일에서 키에 접근하여 메시지 객체를 가져올 수 있고, 객체의 멤버에 접근하여 원하는 메시지를 가져올 수 있습니다.
{ "ProductInfoPage": { "addToCartButton": "장바구니에 담기", "title": "상품 정보", //... 생략 } }
import { useTranslations } from "next-intl"; // ... import 생략 export default function MainPage() { const t = useTranslations("ProductInfoPage"); return ( <div> <h1>{t("title")}</h1> {/* 생략 */} <button>{t("addToCartButton")}</button> </div> ); }
예외적으로 이 프로젝트에서 사용한 방법
그러나 사내에서는 몇 가지 불편한 점이 있어서 컴포넌트에서 개별적으로 텍스트를 관리하고 있습니다.
- 우선 초기 개발 과정에서 프로젝트의 경로나 페이지의 이름이 계속 변경될 수 있어서 json의 키를 관리하기 불편하다는 점 (일반적으로 page 이름으로 키 값을 정함)
- 상황에 따라 페이지 이름이 중복될 경우 키 값으로 페이지를 사용하기 어려운 점
- 컴포넌트에서 멀리 떨어져 있는 json 파일에서 컨텐츠를 확인해야 해서 개발 시 불편하다는 점 (i18n-ally로 해결할 수 있는 문제)
- 하나의 json에 저장해야 할 내용이 너무 많고, 그에 따라 중복되는 텍스트를 관리하기 어렵다는 점
나중에 json 한 파일에 모든 메시지를 저장하려면 next-intl 또는 i18n-ally 플러그인의 기능이 개선될 필요가 있습니다.
- Next.js가 경로 단위로 페이지를 생성하므로, 경로 단위로 번역 메시지를 관리할 수 있는 옵션이 필요함
- 또한 개별 컴포넌트에서도 다양한 언어를 지원해야 하므로 컴포넌트 단위로도 번역 메시지를 관리할 수 있는 기능이 필요함
- 경로나 컴포넌트가 변경되어도 json이 연동되어서 문제가 발생하지 않도록 해야 함
굳이 json 파일 하나에서 모든 메시지를 관리해야 한다면, key 값은 페이지 경로나 컴포넌트 경로를 사용하는 것으로 일단 문제를 해결할 수 있습니다. 그러나 사람이 수동으로 경로를 입력하면서 오타가 발생할 여지가 있으므로, json에 키를 추가할 때 경로 자동완성 기능이 있으면 좋겠습니다.
4. 사용하면서 느낀 next-intl의 장단점
장점
- 호환성: 최신 Next.js 15 버전과 잘 호환되므로, 새로운 Next.js 프로젝트에도 다국어 기능을 적용하기 유리합니다.
- 쉬운 사용
- Json 기반의 메시지 관리 방식으로 코드와 번역 파일을 분리하여 관리합니다
- 또한 Next.js의 라우터 API를 래핑하여 기존 Next.js 유저에게 익숙한 사용성을 제공합니다.
- 활발한 커뮤니티: github에서 자주 업데이트가 이루어지며, 공식 문서도 상세하게 정리되어 있어 학습 곡선이 낮습니다.
- VS Code 확장 프로그램: i18n-ally으로 번역 상태를 시각적으로 확인하거나, VS Code와 통합된 GUI로 번역 메시지를 쉽게 편집할 수 있습니다.
단점
- 언어 별로 하나의 json 파일에 모든 메시지를 관리하는 경우, 컴포넌트의 수가 많아지고 페이지 경로가 복잡해질 수록 유지보수하기 불리합니다.
- json 파일 하나로 모든 메시지를 관리하는 이유 중 하나는 crowdin과 같은 외부 번역 툴이 선호하는 방식이기 때문입니다. 그래서 이러한 단점은 번역 편의를 높이기 위한 트레이드 오프로 볼 수 있습니다.
- 따라서 json의 키 값을 경로 기반으로 정하거나, 또는 적절한 네이밍 컨벤션을 정해서 유지보수가 용이하도록 json 키 값을 관리할 필요가 있습니다.
- 언어 별로 여러 개의 json 파일을 만들 수 있으나, i18n-ally에서 여러 개로 분리된 json 파일을 정상적으로 인식하지 못합니다. 그래서 확장 프로그램의 편의 기능을 이용할 수 없다는 단점이 있습니다.
(부록) next-intl 예제 프로젝트: 반려 동물 소개 웹사이트
next-intl 홈페이지에서 여러 단계에 걸쳐 친절하게 설명되어 있으며 (링크), 참고할 만한 샘플 프로젝트 저장소도 제공하고 있습니다. (링크) 다만 2024년 12월 30일 기준 Next.js 14 이전 버전 기준으로 소개되어 있습니다. 그래서 아래 반려 동물 소개 웹사이트 예제와 함께 최신 Next.js 15 기준 next-intl 프로젝트 세팅 방법과 라이브러리 사용법을 살펴보겠습니다.
next-intl-practice
wodndb • Updated Dec 30, 2024
아래 코드 블록에서 생략된 코드의 자세한 내용은 위 깃허브 저장소를 참고해주세요.
Next.js 프로젝트 생성 및 next-intl 설치
# Next.js 프로젝트 생성 명령어 npx create-next-app@latest # next-intl 패키지 설치 npm install next-intl
필요한 파일 및 폴더 준비
생성된 프로젝트 폴더에서 아래 구조와 같이 필요한 파일과 폴더를 추가해주세요.
├── messages (1) │ ├── en.json │ ├── jp.json │ └── ko.json ├── next.config.ts (2) └── src ├── i18n │ ├── routing.ts (3) │ └── request.ts (5) ├── middleware.ts (4) └── app └── [locale] ├── layout.tsx (6) └── page.tsx (7)
(1) message 폴더
message 폴더에는 아래 예시와 같이 다국어 서비스에서 사용할 텍스트를 언어 별로 구분한 json 파일을 저장합니다. (자세한 내용은 github에서 확인할 수 있습니다.)
{ "MainPage": { "pageTitle": "우리의 사랑스러운 반려 동물들", "description": "우리의 반려 동물들을 소개합니다.", //... 생략 } }
(2) next.config.ts
nextConfig에 next-intl의 플러그인을 적용합니다. 이 플러그인을 적용하면 서버 컴포넌트에 요청 별 i18n 설정을 제공하기 위한 별칭(alias, 복잡하고 긴 경로를 사용하기 편리하게 생략한 짧은 문자열)를 생성합니다.
import type { NextConfig } from "next"; import createNextIntlPlugin from "next-intl/plugin"; const withNextIntl = createNextIntlPlugin(); const nextConfig: NextConfig = { /* config options here */ }; export default withNextIntl(nextConfig);
(3) src/i18n/routing.ts
next-intl은 Next.js에서 locale을 쉽게 다루기 위해서 Next.js의 라우팅 API들을 래핑했습니다. 아래와 같이 middleware에서 사용할 라우팅 설정과 Next.js 프로젝트 내에서 라우팅 API들을 routing.ts에 준비합니다.
import { defineRouting } from "next-intl/routing"; import { createNavigation } from "next-intl/navigation"; export const routing = defineRouting({ locales: ["en", "ko", "jp"], defaultLocale: "ko", }); export type Locale = (typeof routing.locales)[number]; export const { Link, redirect, usePathname, useRouter, getPathname } = createNavigation(routing);
routing
객체: middleware에서 사용할 라우팅 설정입니다. 지원할 locale 목록과 기본 locale을 지정합니다.
Locale
: 타입 검사를 하기 위해 만든 locale 타입입니다. 필수는 아닙니다.
createNavigation
: Next.js에서 제공하는 기본 라우팅 API 들을 래핑하여 반환합니다. 반환된 API는 Next.js 프로젝트 내에서 다양하게 활용될 수 있습니다.
(4) src/middleware.ts 추가
createMiddleware
함수를 사용하여 경로 요청에 대한 locale을 간단하게 핸들링할 수 있습니다. (예: /
→ /ko
)import createMiddleware from "next-intl/middleware"; import { routing } from "./i18n/routing"; export default createMiddleware(routing); export const config = { // Match only internationalized pathnames matcher: ["/", "/(ko|en|jp)/:path*"], };
- 만약 별도의 middleware 로직이 필요하다면, 아래와 같이 요청을 핸들링할 수 있습니다.
import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import { routing } from "./i18n/routing"; const handleI18nRouting = createMiddleware(routing); export function middleware(request: NextRequest) { // do something return handleI18nRouting(request); } export const config = { // Match only internationalized pathnames matcher: ["/", "/(ko|en|jp)/:path*"], };
(5) src/i18n/request.ts
서버 컴포넌트에서 next-intl 기능을 가져와서 사용할 때, 이 파일에 있는 설정을 읽어온다고 합니다. locale 값이나 message 파일 경로를 설정하는 용도로 사용되는 것 같습니다.
import { getRequestConfig } from "next-intl/server"; import { Locale, routing } from "./routing"; export default getRequestConfig(async ({ requestLocale }) => { // This typically corresponds to the `[locale]` segment let locale = await requestLocale; // Ensure that a valid locale is used if (!locale || !routing.locales.includes(locale as Locale)) { locale = routing.defaultLocale; } return { locale, messages: (await import(`../../messages/${locale}.json`)).default, }; });
(6) src/app/[locale]/layout.tsx
기존의 src/app 폴더에 있는 layout.tsx를 src/app/[locale]폴더로 이동시킵니다. Next.js 15에서는 locale 세그먼트에 접근하기 위해 async-await을 사용해야 한다는 점에 유의합니다.
import { Locale, routing } from "@/i18n/routing"; import { NextIntlClientProvider } from "next-intl"; import { getMessages } from "next-intl/server"; import { notFound } from "next/navigation"; // ... import 생략 export default async function RootLayout({ children, params, }: Readonly<{ children: React.ReactNode; params: Promise<{ locale: Locale }>; // Next.js 15에서는 Promise로 params를 받아야 합니다. }>) { const locale = (await params).locale; // Ensure that the incoming `locale` is valid if (!routing.locales.includes(locale as Locale)) { notFound(); } const messages = await getMessages(); return ( <html lang={locale}> <body> <NextIntlClientProvider messages={messages}> {children} </NextIntlClientProvider> </body> </html> ); }
(7) src/app/[locale]/page.tsx
- useTranslations 훅을 사용하여 현재 locale 에 맞는 텍스트를 json에서 가져옵니다.
- json 속성에서 하위 속성에 접근할 때 .(점)을 사용합니다. 예를 들어 강아지 설명을 가져오려면 “pets.dot.description”이라는 키워드를 사용합니다. 오브젝트 멤버에 접근하는 것과 유사한데 큰따옴표를 붙여야 한다는 점이 특징입니다.
import { useTranslations } from "next-intl"; // ... import 생략 export default function MainPage() { const t = useTranslations("MainPage"); return ( <div className="h-dvh flex flex-col items-center justify-between"> {/* 생략 */} <section className="w-full text-center p-4"> <h1 className="text-3xl font-bold">{t("pageTitle")}</h1> <p>{t("description")}</p> </section> <section className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3"> <PetCard name={t("pets.dog.name")} description={t("pets.dog.description")} imageUrl={dogImageUrl} /> {/* 생략 */} </section> </main> {/* 생략 */} </div> ); }
드롭 다운 입력을 사용하여 locale 변경하기
locale을 변경할 수 있는 드롭다운 컴포넌트(LocaleDropdown.tsx)를 추가해봅시다.
useLocale
훅으로 현재 locale 값을 가져올 수 있습니다. 참고로 next-intl의useLocale
은 서버 컴포넌트에서도 사용할 수 있습니다.
usePathname
훅으로 [locale] 세그먼트를 제외한 현재 경로를 가져올 수 있습니다. i18n/routing.ts 파일에서 가져온 것임에 유의합니다.
useRouter
훅으로 경로를 이동하거나, locale을 변경할 수 있습니다. i18n/routing.ts 파일에서 가져온 것임에 유의합니다.
"use client"; import { usePathname, useRouter } from "@/i18n/routing"; import { useLocale } from "next-intl"; export default function LocaleDropdown() { const locale = useLocale(); const pathname = usePathname(); const router = useRouter(); const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => { const locale = e.target.value; router.push({ pathname }, { locale }); }; return ( <select value={locale} onChange={handleChange} className="border rounded p-1" > <option value="ko">한국어</option> <option value="en">English</option> <option value="jp">日本語</option> </select> ); }
결과



4. next-intl 사용 팁
VSCode 확장 플러그인: i18n-ally
next-intl을 사용할 때 가장 번거로운 점은 컴포넌트 코드를 다루면서 번역될 텍스트의 내용을 미리 확인해볼 수 없다는 점입니다. VS Code에서 next-intl을 지원하는 확장 플러그인을 설치하면 이러한 문제를 해결할 수 있습니다. 플러그인을 통해 컴포넌트 코드에서 json에 있는 메시지의 내용을 미리 확인할 수 있으며, json 파일을 열지 않아도 메시지가 사용되는 위치에서 메시지를 쉽게 편집할 수 있습니다.
- json에 저장된 텍스트 메시지를 코드 상에 표시하고, 커서를 호버하면 바로 번역 또는 편집하는 GUI를 띄우는 기능

- 미리 볼 수 있는 메시지의 locale을 전환하는 기능

- 하드코딩된 텍스트를 json으로 추출하는 기능

- 번역 메시지를 일괄적으로 관리하고 상태를 확인할 수 있는 탭

next-intl의 navigation API 사용하기
앞서 예제 프로젝트에서도 확인했지만, Next.js의 라우팅 API를 사용하지 않고, next-intl의
createNavigation
으로 만든 라우팅 API 함수들을 활용하여 경로를 탐색(navigate)했습니다. 이 함수들은 Next.js의 라우팅 API를 래핑한 것이므로 사용법이 Next.js의 것과 거의 동일합니다.next-intl의 라우팅 API를 사용하면
locale
을 쉽게 변경할 수 있으며 경로를 탐색할 때 locale을 신경쓰지 않아도 됩니다. 그래서 경로를 탐색해야 하는 대부분의 경우에는 next-intl의 createNavigation
에서 제공하는 라우팅 API를 사용하는 것을 권장합니다. 또한 Next.js의 라우팅 API를 사용하면 경로 탐색 시 locale
변경이 정상적으로 반영되지 않을 수 있으므로 주의가 필요합니다.자세한 내용은 아래 next-intl의 문서를 참고해주세요.
notFound 페이지 커스텀
next-intl 구조에서는 일반적으로 모든 layout과 page가 [locale] 경로 세그먼트의 하위에 배치됩니다. 그래서 Next.js에서 기본적으로 제공하는 404 not found 페이지를 커스텀하려면 별도의 코드를 추가해야 합니다. 자세한 내용은 아래 next-intl의 문서를 참고해주세요.