Skip to content

Nextjs i18n

NextJs 实现I18n

国际化框架:https://next-intl-docs.vercel.app/

创建NextJs新项目

Terminal window
-> % 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

安装国际化框架依赖

Terminal window
-> % 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 configuration
export 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