Làm thế nào để cấu hình đa ngôn ngữ đối với NextJS ?
Trong hướng dẫn này, mình sẽ hướng dẫn cấu hình và triển khai đa ngôn ngữ (i18n)
trong ứng dụng NextJS của mọi người.
1. Bắt đầu
> Việc cấu hình đa ngôn ngữ (i18n)
còn tuỳ thuộc vào version của NextJS mà mọi người đang sử dụng:
- App Router
- Page Router
> Một vài example mà mọi người có thể tham khảo: Nhấn vào đây
1.1 App Router
> Đối với App router, sẽ có 2 loại cấu hình, tuỳ thuộc vào mục đích của mọi người.
Có i18n routing
Để sử dụng các unique pathname cho mỗi ngôn ngữ mà ứng dụng của mọi người đang muốn triển khai, thì next-intl
sẽ xử lý được những vấn đề như sau
- Dựa trên prefix ở pathname (ví dụ: /en/about hoặc /vi/about ...)
- Dựa trên tên miền (ví dụ: en.example.com/about hoặc vi.example.com/about)
Trong cả hai trường hợp trên, next-intl
tích hợp với App Router bằng cách sử dụng một [locale]
ở layout cao nhất, vì vậy có thể được sử dụng để cung cấp nội dung
đối với những ngôn ngữ mà mọi người đang triển khai.
- Đầu tiên, mọi người cần setup một ứng dụng bằng
NextJS App Router
trước nhé, nếu chưa thì hãy tham khảo ở document này nhé.
npm install next-intl
- Ta sẽ cấu hình folder của source theo cấu trúc sau:
├── dictionaries (1)
│ ├── en.json
│ └── vi.json
│ └── ...
├── next.config.mjs (2)
└── src
├── i18n.ts (3)
├── middleware.ts (4)
└── app
└── [locale]
├── layout.tsx (5)
└── page.tsx (6)
1) Dictionaries
- Nội dung có thể được lưu trữ ở local folder hoặc download từ các remote resources (ví dụ hệ thống translation management chẳng hạn).
- Tuỳ theo nhu cầu và mục đích sử dụng mà bạn chọn cách lưu trữ phù hợp nhé.
- Với mình, thì mình sẽ chọn cách đơn giản là lưu ở local folder, tạo ra những file JSON với từng ngôn ngữ (vi.json, en.json,...).
// dictionaries/vi.json
{
"HomePage": {
"title": "Hello world!",
"description": "Xin Chàoo !"
},
...
}
2) Set up next config
- setup plugin tạo ra các alias để cung cấp cấu hình i18n của mọi người cho các Server Components
await import("./src/env.js");
import nextIntl from "next-intl/plugin";
const withNextIntl = nextIntl();
const nextConfig = {...};
export default withNextIntl(nextConfig);
3) i18n
next-intl
tạo object dùng để được sử dụng để cung cấp các dictionaries và options khác dựa trên ngôn ngữ mà mọi người cấu hình ởdictionaries
để sử dụng ở Server Components
import { getRequestConfig } from "next-intl/server";
import { notFound } from "next/navigation";
// Can be imported from a shared config
const locales: string[] = ["vi", "en"];
export default getRequestConfig(async ({ locale }) => {
// Validate that the incoming `locale` parameter is valid
if (!locales.includes(locale)) notFound();
return {
messages: (await import(`../dictionaries/${locale}.json`)).default
}
});
4) middlewares
- Xử lí routing,etc... với từng locale mà mọi người định nghĩa.
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ["vi", "en"],
defaultLocale: "vi",
});
export const config = {
// Match only internationalized pathnames
matcher: ['/', '/(vi|en)/:path*']
}
- Ngoài ra, mọi người cũng có thể kết hợp nhiều middlewares lại với nhau, ví dụ như sử dụng middlewares như NextAuth, Clerk, ... với Next-Intl middlewares...
- Tham khảo một vài ví dụ về combine middlewares ở đây nha
> Ví dụ về Clerk middleware
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
import createMiddleware from "next-intl/middleware";
const intlMiddleware = createMiddleware({
locales: ["vi", "en"],
defaultLocale: "vi",
});
const isProtectedRoute = createRouteMatcher(["/(auth)(.*)"]);
export default clerkMiddleware((auth, req) => {
if (isProtectedRoute(req)) auth().protect();
return intlMiddleware(req);
});
export const config = {
matcher: ["/", "/(vi|en)/:path*"],
};
5) 'app/[locale]/layout.tsx'
- Mọi người có thể sử dụng phần này để pass config từ
i18n.ts
đến các components khác thông quaNextIntlClientProvider
.
import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';
export function generateStaticParams() {
return locales.map((locale) => ({ locale }));
}
export default async function LocaleLayout({
children,
params: {locale}
}: {
children: React.ReactNode;
params: {locale: string};
}) {
// Providing all messages to the client
// side is the easiest way to get started
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
Lưu ý rằng là
""NextIntlClientProvider
sẽ tự động kế thừa những config từi18n.ts
ở đây nhé !
6) Sử dụng
- Sử dụng useTranslation ở bất cứ components nào trong applications của mọi người, miễn là phải nằm trong
NextIntlClientProvider
nhé.
import {useTranslations} from 'next-intl';
export default function HomePage() {
const t = useTranslations('HomePage');
return <h1>{t('title')}</h1>;
}
import {useTranslations} from 'next-intl';
export default async function HomePage() {
const t = await getTranslation('HomePage');
return <h1>{t('title')}</h1>;
}
Không có i18n routing
- Đối với việc không triển khai routing với
i18n
, thì các bước thực hiện đơn giản hơn việc chia routing - Cũng thực hiện theo những
step 1
,step 2
,step 3
như cách chia routing, tuy nhiên, ở đây sẽ không cần config middlewares.
4) 'app/layout.tsx'
import {NextIntlClientProvider} from 'next-intl';
import {getLocale, getMessages} from 'next-intl/server';
export default async function RootLayout({
children
}: {
children: React.ReactNode;
}) {
const locale = await getLocale();
// Providing all messages to the client
// side is the easiest way to get started
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
5) Sử dụng
- Sử dụng useTranslation ở bất cứ components nào trong applications của mọi người, miễn là phải nằm trong
NextIntlClientProvider
nhé.
import {useTranslations} from 'next-intl';
export default function HomePage() {
const t = useTranslations('HomePage');
return <h1>{t('title')}</h1>;
}
2. Tổng kết
- Vậy là đã xong về những bước cơ bản để setup một dự án NextJS đa ngôn ngữ với i18n.
- Những gì mình viết và chia sẻ đều được vận dụng từ dự án thực tế mà mình đã làm và tham khảo tài liệu của Next-Intl
- Ngoài ra, docs của
Next-Intl
có những kiến thức hay khác như là:- sử dụng với Server & Client Components
- sử dụng với metadata
- MDX
- Navigation
- Middlewares
- ...
Hi vọng những gì mình chia sẻ ở trên, sẽ giúp ích cho mọi người trong quá trình làm việc. Cảm ơn vì đã xem bài viết của mình nhé. Se yaaa !
nextjs
i18n
next-intl
frontend
react