Tối ưu hóa Quốc tế hóa Next.js với Đường dẫn URL
Trong bài viết này, chúng ta sẽ khám phá quy trình tối ưu hóa việc xử lý ngôn ngữ trong ứng dụng Next.js. Phiên bản ban đầu của chúng tôi dựa vào cách tiếp cận sử dụng cookie cho quốc tế hóa, điều này đã dẫn đến việc phải render động và giảm hiệu suất cũng như hiệu quả SEO.
Chuyển sang chiến lược định tuyến dựa trên ngôn ngữ, trong đó ngôn ngữ được nhúng trực tiếp vào đường dẫn URL (ví dụ: /en/about
, /ja/about
), cho phép Next.js pre-render các đường dẫn cụ thể theo ngôn ngữ ngay tại thời điểm xây dựng. Điều này đã khôi phục lại các lợi ích của việc tạo trang tĩnh (SSG), kết quả là thời gian tải nhanh hơn và cải thiện SEO.
Khai thác Định tuyến Dựa trên Ngôn ngữ
Việc nhúng ngôn ngữ trực tiếp vào đường dẫn URL giải quyết được nhiều thách thức. Mỗi đường dẫn cụ thể theo ngôn ngữ trở thành một đường dẫn tĩnh có thể được pre-render tại thời điểm xây dựng. Ví dụ, /en/about
và /ja/about
được coi là các trang tĩnh riêng biệt, mỗi trang có bản dịch tương ứng.
Cách tiếp cận này đơn giản hóa logic điều hướng và nâng cao SEO. Các URL có thể chia sẻ và đánh dấu với ngữ cảnh ngôn ngữ chính xác, và các công cụ tìm kiếm có thể lập chỉ mục từng ngôn ngữ riêng biệt, cải thiện khả năng tìm thấy.
Chúng ta có thể thực hiện điều này một cách sạch sẽ bằng cách tách cấu hình next-intl
của chúng ta thành các file riêng biệt.
1. Cấu hình Ngôn ngữ (config.ts
)
Đầu tiên, chúng ta tạo một file cấu hình trung tâm để xác định các ngôn ngữ và tên của chúng. Điều này thúc đẩy khả năng tái sử dụng mã và giúp dễ dàng thêm hoặc xóa ngôn ngữ trong tương lai.
typescript
export type Locale = 'en' | 'ja';
export const locales: Locale[] = ['en', 'ja'] as const;
export const defaultLocale: Locale = 'en';
export const localeNames: Record<Locale, string> = {
en: "EN",
ja: "日本"
} as const;
2. Thiết lập Định tuyến (routing.ts
)
Tiếp theo, chúng ta xác định chiến lược định tuyến của mình. Bằng cách đặt localePrefix: 'never'
, chúng ta rõ ràng thông báo cho next-intl
rằng chúng ta đang xử lý các tiền tố ngôn ngữ của chính mình thông qua đoạn động [locale]
của Next.js. Điều này ngăn next-intl
thêm một tiền tố extra vào URL của chúng ta.
typescript
import { defineRouting } from 'next-intl/routing';
import { defaultLocale, locales } from './config';
export const routing = defineRouting({
locales: locales,
defaultLocale: defaultLocale,
localePrefix: 'never'
});
Lưu ý: Tùy chọn localePrefix
kiểm soát việc ngôn ngữ có xuất hiện trong đường dẫn URL hay không:
'never'
→ ngôn ngữ không xuất hiện trong URL (/about
cho tất cả các ngôn ngữ).'always'
→ ngôn ngữ luôn xuất hiện (/en/about
,/ja/about
).'as-needed'
→ ngôn ngữ chỉ xuất hiện nếu nó không phải là ngôn ngữ mặc định (/about
cho mặc định,/ja/about
cho tiếng Nhật).
Việc chọn tùy chọn phù hợp phụ thuộc vào việc SEO dựa trên URL và các liên kết có thể chia sẻ cho mỗi ngôn ngữ có quan trọng hay không.
3. Xử lý Điều hướng (navigation.ts
)
Chúng ta sau đó sử dụng cấu hình định tuyến đã định nghĩa để tạo các trợ giúp điều hướng tùy chỉnh. Điều này đảm bảo rằng mỗi khi chúng ta sử dụng Link
, useRouter
, hoặc các chức năng điều hướng khác, chúng tự động tôn trọng thiết lập định tuyến dựa trên ngôn ngữ của chúng ta.
typescript
import { createNavigation } from 'next-intl/navigation';
import { routing } from './routing';
export const { Link, redirect, usePathname, useRouter, getPathname } =
createNavigation(routing);
4. Giải quyết Ngôn ngữ Bên server (request.ts
)
File request.ts
xử lý logic bên server để lấy dữ liệu cụ thể theo ngôn ngữ. Chúng ta sử dụng getRequestConfig
để xác định ngôn ngữ chính xác từ yêu cầu và tải các file tin nhắn tương ứng. Hàm trợ giúp hasLocale
là rất quan trọng ở đây để đảm bảo ngôn ngữ hợp lệ đang được yêu cầu, quay lại ngôn ngữ mặc định nếu không.
typescript
import { getRequestConfig } from 'next-intl/server';
import { hasLocale } from 'next-intl';
import { routing } from './routing';
export default getRequestConfig(async ({ requestLocale }) => {
const requested = await requestLocale;
const locale = hasLocale(routing.locales, requested)
? requested
: routing.defaultLocale;
const messages = (await import(\`@/i18n/locales/\${locale}/common.json\`)).default;
return {
locale,
messages,
};
});
5. Bố cục Gốc (layout.ts
)
Bố cục gốc của chúng ta xử lý thiết lập nền tảng cho mỗi trang. Chúng ta sử dụng setRequestLocale(locale)
để thông báo cho next-intl
sử dụng ngôn ngữ từ URL. Điều này giúp Next.js tận dụng việc render tĩnh vì ngôn ngữ đã được biết tại thời điểm yêu cầu. Kiểm tra hasLocale
cũng giúp chúng ta bắt các ngôn ngữ không hợp lệ và phục vụ một trang notFound()
.
typescript
import "@/index.css";
import { geistSans, geistMono } from "@/constants/fonts";
import { metadata } from "@/constants/metadata";
import { NextIntlClientProvider, hasLocale } from "next-intl";
import { routing } from "@/i18n/routing";
import { notFound } from "next/navigation";
import { setRequestLocale } from "next-intl/server";
export { metadata };
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;
if (!hasLocale(routing.locales, locale)) {
notFound();
}
setRequestLocale(locale);
return (
<html lang={locale}>
<body>
<NextIntlClientProvider locale={locale}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
6. Bộ chuyển đổi Ngôn ngữ phía Khách (locale-switcher.tsx
)
Component này cung cấp một cách dễ sử dụng để thay đổi ngôn ngữ. Nó sử dụng useRouter
và usePathname
tùy chỉnh từ file navigation.ts
. Khi người dùng chọn một ngôn ngữ mới, lệnh gọi router.push
tự động cập nhật đường dẫn URL để phản ánh ngôn ngữ mới, ví dụ: từ /en/about
sang /ja/about
.
typescript
"use client";
import { useLocale } from "next-intl";
import { locales, localeNames, type Locale } from "@/i18n/config";
import { usePathname, useRouter } from "@/i18n/navigation";
export default function LocaleSwitcher() {
const locale = useLocale();
const pathname = usePathname() || "/";
const router = useRouter();
const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const newLocale = event.target.value as Locale;
router.push(pathname, { locale: newLocale });
};
return (
<div className="flex items-center space-x-2" aria-label="Bộ chuyển đổi ngôn ngữ">
<label htmlFor="locale-select" className="sr-only">
Chọn ngôn ngữ
</label>
<select
id="locale-select"
value={locale}
onChange={handleChange}
className="border border-gray-300 rounded p-1 text-blue-500 focus:outline-none focus:ring focus:ring-blue-300"
>
{locales.map((loc) => (
<option key={loc} value={loc}>
{localeNames[loc]}
</option>
))}
</select>
</div>
);
}
7. Xử lý Đường dẫn Không hợp lệ
Một đường dẫn catch-all đảm bảo xử lý nhất quán đối với các URL không hợp lệ hoặc ngôn ngữ không được hỗ trợ:
Thực hiện
- Tạo một Đường dẫn Catch-All: Tạo một thư mục mới và một file
page.tsx
tại gốc thư mụcapp
của bạn. Thư mục nên được đặt tên là[...rest]
để tạo một đoạn catch-all. Cấu trúc này sẽ đảm bảo rằng bất kỳ yêu cầu nào không khớp với file hoặc thư mục hiện có trong ứng dụng của bạn sẽ được bắt bởi đường dẫn này.
/app
├── /[locale]
│ └── page.tsx
├── /[...rest]
│ └── page.tsx
└── ...
- Thực hiện Trang Catch-All: Bên trong file
app/[...rest]/page.tsx
, bạn có thể render trang không tìm thấy tùy chỉnh của mình. Bằng cách sử dụngnotFound()
từnext/navigation
, bạn thông báo cho Next.js hiển thị thành phầnnot-found.tsx
tùy chỉnh mà bạn đã định nghĩa ở cấp độ cao nhất trong thư mụcapp
của mình.
typescript
// app/[...rest]/page.tsx
import { notFound } from 'next/navigation';
export default function CatchAllPage() {
notFound();
}
Kết luận
Bằng cách chuyển từ hệ thống ngôn ngữ dựa trên cookie sang định tuyến dựa trên URL, việc tạo trang tĩnh được phục hồi hoàn toàn trong khi vẫn giữ cho quốc tế hóa liền mạch và thân thiện với SEO. Cấu trúc mô-đun đơn giản hóa việc thêm ngôn ngữ mới, đảm bảo định tuyến nhất quán và cải thiện hiệu suất tổng thể của ứng dụng.
Tham khảo
Câu hỏi thường gặp
1. Tại sao nên sử dụng định tuyến dựa trên URL cho quốc tế hóa?
Sử dụng định tuyến dựa trên URL cho phép Next.js pre-render các đường dẫn theo ngôn ngữ, cải thiện hiệu suất và SEO.
2. Làm thế nào để thêm ngôn ngữ mới vào ứng dụng Next.js?
Bạn chỉ cần cập nhật file cấu hình ngôn ngữ và thêm bản dịch cho ngôn ngữ mới vào thư mục i18n.
3. Làm thế nào để xử lý các lỗi trong định tuyến?
Bạn có thể sử dụng các trang catch-all để xử lý các URL không hợp lệ và hiển thị trang không tìm thấy tùy chỉnh.