
在 NextJS 中使用 i18n 的指南
目前專案在 NextJS App router, SSG/ISR 環境中 使用到 i18n, 此篇紀錄設定
安裝需求
在專案中安裝以下套件:
- i18next:核心國際化框架
- react-i18next:React 綁定
- i18next-browser-languagedetector:用於檢測瀏覽器語言
- i18next-resources-to-backend:用於動態加載翻譯資源
- react-cookie:用於管理語言偏好設置的 cookie
Installation
npm install i18next react-i18next i18next-browser-languagedetector i18next-resources-to-backend react-cookie
配置步驟
1. 路徑和文件夾設置
在 app 文件夾中設置動態路由文件夾 [lng]。所有其他路徑(文件夾)將存儲在此文件夾下。
app
├── [lng]
│ └── 所有路徑文件夾
└── i18n
├── locales
├── client.ts // Client Component 的配置和 useTranslation 函數
├── index.ts // Server Component 的配置和 useTranslation 函數
├── setting.ts
└── lang.ts // 從 URL 獲取當前語言2. 建立 i18n 配置文件
client.ts : 建立Client Component i18n 設置的配置文件
"use client";
import { useEffect, useState } from "react";
import i18next from "i18next";
import {
initReactI18next,
useTranslation as useTranslationOrg,
} from "react-i18next";
import { useCookies } from "react-cookie";
import resourcesToBackend from "i18next-resources-to-backend";
import LanguageDetector from "i18next-browser-languagedetector";
import { getOptions, languages, cookieName } from "./setting";
// 檢查代碼是否在Server端運行
const runsOnServerSide = typeof window === "undefined";
// 初始化 i18next
i18next
.use(initReactI18next) // 將 i18n 傳遞給 react-i18next
.use(LanguageDetector) // 使用語言檢測器
.use(
resourcesToBackend(
(lng: string, ns: string) => import(`./locales/${lng}/${ns}.json`)
)
) // 動態加載翻譯
.init({
...getOptions(),
lng: undefined, // 讓Client檢測語言
detection: {
order: ["path", "htmlTag", "cookie", "navigator"],
},
preload: runsOnServerSide ? languages : [], // 在Server端預加載所有語言
});
// 自定義 useTranslation hook
export function useTranslation(lng?: string, ns?: string, options?: {}) {
const [cookies, setCookie] = useCookies([cookieName]);
const ret = useTranslationOrg(ns, options);
const { i18n } = ret;
if (runsOnServerSide && lng && i18n.resolvedLanguage !== lng) {
i18n.changeLanguage(lng);
} else {
// Client語言處理
const [activeLng, setActiveLng] = useState(i18n.resolvedLanguage);
// 當解析的語言變更時更新活動語言
useEffect(() => {
if (activeLng === i18n.resolvedLanguage) return;
setActiveLng(i18n.resolvedLanguage);
}, [activeLng, i18n.resolvedLanguage]);
// 當 lng prop 變更時改變語言
useEffect(() => {
if (!lng || i18n.resolvedLanguage === lng) return;
i18n.changeLanguage(lng);
}, [lng, i18n]);
// 當語言變更時更新 cookie
useEffect(() => {
if (cookies.i18next === lng) return;
setCookie(cookieName, lng, { path: "/" });
}, [lng, cookies.i18next, setCookie]);
}
return { ...ret };
}3. index.ts : 建立Server Component i18n 設置的配置文件
// 默認語言
export const fallbackLng = "en";
// 支持的語言
export const languages = [fallbackLng, "zh-Hant"];
// 存儲語言偏好的 cookie 名稱
export const cookieName = 'i18next'
// 默認命名空間
export const defaultNS = "translation";
// 獲取 i18next 選項的函數
export function getOptions(lng = fallbackLng, ns = defaultNS) {
return {
debug: true,
supportedLngs: languages,
fallbackLng,
lng,
fallbackNS: defaultNS,
defaultNS,
ns,
};
}4. 建立翻譯文件
在 app/i18n/locales/ 目錄下為每種語言建立翻譯文件,例如:
- app/i18n/locales/en/common.json
- app/i18n/locales/zh/common.json
5. 使用翻譯
在Client Component 中:
"use client";
import { useTranslation } from "../i18n/client";
import { CurrentLang } from "../i18n/lang";
export default function ClientComponent() {
const lang = CurrentLang(); // 獲取當前語言
const { t } = useTranslation(lang, "common");
return <h1>{t('welcome')}</h1>; // 使用翻譯
}在Server端 Component 中:
import { useTranslation } from "../i18n";
export default async function ServerComponent({ params: { lng } }) {
const { t } = await useTranslation(lng, "common");
return <h1>{t('welcome')}</h1>; // 使用翻譯
}注意事項
- 確保在 next.config.js 中正確配置了 i18n 設置。
- 對於動態路由,確保在 app/[lng] 文件夾中正確處理語言設置。
- 獲取當前語言設置:可以從 params 傳下來,若無法從 params 傳下來,則使用 CurrentLang() 函數來獲取當前語言設置。
- 在Client Component 和Server端 Component 中使用不同的 useTranslation 導入方式。
- Client和Server端的 i18next 配置略有不同,需要注意區分使用。