Nextjs i18n
NextJs 实现I18n
国际化框架:https://next-intl-docs.vercel.app/
创建NextJs新项目
-> % npx create-next-app@latest✔ What is your project named? … appname✔ Would you like to use TypeScript? … No / [Yes]✔ Would you like to use ESLint? … No / [Yes]✔ Would you like to use Tailwind CSS? … No / [Yes]✔ Would you like to use `src/` directory? … No / [Yes]✔ Would you like to use App Router? (recommended) … No / [Yes]✔ Would you like to customize the default import alias (@/*)? … [No] / Yes
安装国际化框架依赖
-> % npm install next-intl
项目目录
├── messages +│ ├── en.json (1) +│ └── zh.json (1.1) +├── next.config.mjs (2)└── src ├── i18n + │ ├── routing.ts (3) + │ └── request.ts (5) + ├── middleware.ts (4) + └── app └── [locale] ├── layout.tsx (6) └── page.tsx (7)
配置国际化消息
在messages目录下创建en.json和zh.json文件,用于存储国际化消息。
{ "welcome": "Welcome to Next.js!"}
配置中间件 src/middleware.ts
import createMiddleware from 'next-intl/middleware';import {routing} from './i18n/routing';
export default createMiddleware(routing);
export const config = { // Match only internationalized pathnames matcher: ['/', '/(zh|en)/:path*']};
配置国际化路由 src/i18n/routing.ts
在i18n目录下创建routing.ts文件,用于配置国际化路由。
import {defineRouting} from 'next-intl/routing';import {createSharedPathnamesNavigation} from 'next-intl/navigation';
export const routing = defineRouting({ // A list of all locales that are supported locales: ['en', 'de'],
// Used when no locale matches defaultLocale: 'en'});
// Lightweight wrappers around Next.js' navigation APIs// that will consider the routing configurationexport const {Link, redirect, usePathname, useRouter} = createSharedPathnamesNavigation(routing);
配置国际化布局 src/i18n/request.ts
import {notFound} from 'next/navigation';import {getRequestConfig} from 'next-intl/server';import {routing} from './routing';
export default getRequestConfig(async ({locale}) => { // Validate that the incoming `locale` parameter is valid if (!routing.locales.includes(locale as any)) notFound();
return { messages: (await import(`../../messages/${locale}.json`)).default };});
配置国际化页面 src/app/[locale]/layout.tsx
增加params参数{locale: string}
import type { Metadata } from "next";import localFont from "next/font/local";import "../globals.css";
const geistSans = localFont({ src: "../fonts/GeistVF.woff", variable: "--font-geist-sans", weight: "100 900",});const geistMono = localFont({ src: "../fonts/GeistMonoVF.woff", variable: "--font-geist-mono", weight: "100 900",});
export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app",};
export default function RootLayout({ children, params: {locale}}: Readonly<{ children: React.ReactNode; params: {locale: string};}>) { return ( <html lang="en"> <body className={`${geistSans.variable} ${geistMono.variable} antialiased`} > {children} </body> </html> );}
静态化页面并使用国际化标题
├── src│ ├── app│ │ ├── [locale]│ │ │ └── wheel│ │ │ └── [template]│ │ │ └── page.tsx
import WheelPage from '@/components/wheel/WheelPage'import { templates } from '@/libs/WheelConstants'import React, { useEffect } from 'react'import { useConfigStore } from '../../../../../config-store-provider';import WheelQAs from '@/components/WheelQAs';import { GetStaticPaths, GetStaticProps, Metadata } from 'next';import { getMessages, unstable_setRequestLocale } from 'next-intl/server';import { useTranslations } from 'next-intl';import languages from '@/libs/languages';import {routing} from '@/i18n/routing';
// 生成静态路径export async function generateStaticParams() { const locales = routing.locales.map((locale) => ({locale})).map((item) => item.locale); console.log("locales===>", locales);
const tpls = templates.map(t => t.id);
return tpls.flatMap((template) => locales.map((locale) => ({ template, locale, })) );}
// 生成静态路径export async function generateMetadata({ params }: { params: { template: string, locale: string} }): Promise<Metadata> { const template = templates.find(t => t.id === params.template); const messages = await getMessages({ locale: params.locale });
// 确保 messages 是包含 WheelController 的类型 const wheelTemplates = (messages as any).WheelController.templates; const siteTitle = (messages as any).Site.title; const siteDescription = (messages as any).Site.description; const title = wheelTemplates.find((t: { id: string }) => t.id === params.template)?.name;
return { title: title ? `${title} - ${siteTitle}` : siteTitle, description: siteDescription, };}
function page({ params }: { params: { template: string, locale: string } }) { unstable_setRequestLocale(params.locale);
const template = templates.find(t => t.id === params.template)
return ( <main className=''> {/* <span>{params.locale}</span> */} <WheelPage template={template as WheelTemplate}/> </main> )}
export default page