
为你的Next.js静态网站国际化(使用App Router)
Next.js提供的一个令人惊叹的功能是静态导出(Static Export)。启用静态导出后,Next.js会基于你的整个React应用程序生成静态的HTML/CSS/JS文件。
关注我带你了解科技领域最新的技术与产品。
这种方法有很多好处,尤其是对于性能和SEO方面。这也意味着你的应用程序可以部署和托管在任何可以提供HTML/CSS/JS静态资源的Web服务器上,比如一个简单而实惠的nginx配置。
在使用静态导出时,我遇到了一个问题,即如何进行国际化(i18n)并将我的应用程序内容翻译成多种语言。我发现在线资源关于这个主题的文章非常少,特别是在使用Next.js 13版本引入的App Router时。
让我们一起创建一个基本的国际化应用程序,演示如何实现这一点!
你可以在这里找到工作演示项目:
https://github.com/RockyStrongo/next-i18n-static/
第一步:初始化项目
运行以下命令创建一个新的Next.js项目,并按照提示进行操作。
npx create-next-app@latest
我们将在这个示例中使用TypeScript、Tailwind和App Router。
现在,我们可以开始用我们应用程序的内容替换默认项目。
我们的要求如下:
一个带有两个链接(Home和About)的导航栏
一个包含文本“Hello World”的主页
一个包含文本“这是一个完全翻译的静态网站”的关于页面
所有文本都应该用英语和法语翻译
导航栏中应该有一个语言切换器
1.1 创建一个导航栏组件:
创建一个components文件夹和一个Header组件
// components/Header.tsx
import Link from "next/link";
export default function Header() {
return (
)
}
1.2 在主页上更新为'Hello World'
// app/page.tsx
export default function Home() {
return (
Hello World!
)
}
1.3 在应用程序布局中包含导航栏:
// app/layout.tsx
import './globals.css'
import type { Metadata } from 'next'
import Header from '@/components/Header'
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
{children}
)
}1.4 创建关于页面:
// app/about/page.tsx
export default function AboutPage() {
return (
这个应用是一个完全翻译的静态网站
)
}
1.5 更新 next.config.js 文件以启用静态导出
通过这个配置,在运行 npm run build 时,Next.js 将在 out 文件夹中生成静态的 HTML/CSS/JS 文件。
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'export',
}
module.exports = nextConfig
到目前为止,我们已经实现了应用的基本功能,但是还没有加入任何翻译或国际化逻辑。所有的文本目前都是硬编码的。
步骤2:国际化项目
我们将使用 next-intl 库,该库在 Next.js 社区中得到广泛维护和使用。它在 Next.js 的官方文档中的国际化部分中有提到。
npm install next-intl
2.1 创建翻译文件
文件 messages/en.json
{
"Homepage": {
"helloWorld": "Hello World !"
},
"AboutPage": {
"aboutText": "This is a fully translated static website"
},
"Header": {
"home": "Home",
"about": "About"
}
}
文件 messages/fr.json
{
"HomePage": {
"helloWorld": "Bonjour tout le monde !"
},
"AboutPage": {
"aboutText": "Ceci est un site statique complètement traduit."
},
"Header": {
"home": "Accueil",
"about": "A propos"
}
}
2.2 更新文件结构
├── messages
│ ├── en.json
│ └── fr.json
└── app
└── [locale]
├── layout.tsx
└── page.tsx
└── layout.tsx
└── page.tsx
└── not-found.tsx
首先创建 [locale] 文件夹,并将现有的 page.tsx 文件、layout.tsx 文件和 about 文件夹移动到其中。不要忘记更新导入语句。
创建一个新的 /app/not-found.tsx 文件,这将是我们在用户输入错误的 URL 时显示的错误页面。
// app/not-found.tsx
export default function NotFound() {
return (
自定义404页面
)
}
创建一个新的 /app/layout.tsx 文件:
// app/layout.tsx
import {ReactNode} from 'react';
type Props = {
children: ReactNode;
};
// 因为根目录上有一个 not-found.tsx 页面,所以需要一个布局文件,
// 即使它只是简单地传递子元素。
export default function RootLayout({children}: Props) {
return children;
}
创建一个新的 /app/page.tsx 文件:
在这个页面中,我们将用户重定向到默认语言,对于我们来说是英语。
注意:使用静态导出,不能使用没有前缀的默认语言。我们必须将传入的请求重定向到默认语言。如文档中所述。
// app/page.tsx
import {redirect} from 'next/navigation';
export default function RootPage() {
redirect('/en');
}
2.3 更新 app/[locale]/layout.tsx
// app/[locale]/layout.tsx
import '../globals.css'
import type { Metadata } from 'next'
import Header from '@/components/Header'
import { ReactNode } from 'react'
import { notFound } from 'next/navigation'
import { NextIntlClientProvider } from 'next-intl'
type Props = {
children: ReactNode
params: { locale: string }
}
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
// 获取翻译的函数
async function getMessages(locale: string) {
try {
return (await import(../../messages/${locale}.json)).default
} catch (error) {
notFound()
}
}
// 生成所有语言的路由的函数
export async function generateStaticParams() {
return ['en', 'fr'].map((locale) => ({ locale }))
}
export default async function RootLayout({
children,
params: { locale },
}: Props) {
const messages = await getMessages(locale)
return (
{children}
)
}
我们在这里做了以下工作:
添加了一个名为getMessages的函数,用于获取翻译信息。
添加了一个名为generateStaticParams的函数,用于为所有的语言环境生成静态路由。
添加了上下文提供者NextIntlClientProvider,使得我们的翻译能够在应用的所有页面中使用。
更新了页面和组件,以使用翻译信息。
具体代码如下所示:
tsx
// app/page.tsx
'use client'
import { useTranslations } from 'next-intl'
export default function HomePage() {
const t = useTranslations('HomePage')
return (
{t('helloWorld')}
)
}
tsx
// app/[locale]/about/page.tsx
'use client'
import { useTranslations } from 'next-intl'
export default function AboutPage() {
const t = useTranslations('AboutPage')
return (
{t('aboutText')}
)
}
tsx
// components/Header.tsx
'use client'
import Link from "next/link";
import { useTranslations } from 'next-intl'
export default function Header() {
const t= useTranslations('Header')
return (
{t('home')}
{t('about')}
)
}
更新链接以考虑语言环境前缀,使用'next-intl/link'组件替代Next.js提供的Link组件。
tsx
// components/Header.tsx
'use client'
import Link from 'next-intl/link';
import { useTranslations } from 'next-intl'
export default function Header() {
const t= useTranslations('Header')
return (
{t('home')}
{t('about')}
)
}
如果你运行应用程序,你会注意到你会立即从 http://localhost/3000 被重定向到 http://localhost/3000/en ,头部的链接已经考虑到了'en'前缀。如果你将它从'en'改为'fr',你会看到你的网站被翻译成法语。
现在我们几乎完成了!我们需求列表中只剩下语言切换器!
第3步 - 添加一个语言切换器
创建必要的翻译文件。
messages/en.json:
json
"LocaleSwitcher": {
"locale": "{locale, select, fr {French} en {English} other {Unknown}}"
}
messages/fr.json:
json
"LocaleSwitcher": {
"locale": "{locale, select, fr {français} en {anglais} other {inconnu}}"
}
创建语言切换器组件。
tsx
// components/LocaleSwitcher.tsx
'use client'
import { useLocale, useTranslations } from 'next-intl';
import { usePathname, useRouter } from 'next-intl/client';
export default function LocaleSwitcher() {
const t = useTranslations('LocaleSwitcher')
const locale = useLocale();
const router = useRouter();
const pathname = usePathname();
const onLocaleChange = (e: React.ChangeEvent
const newLocale = e.target.value;
router.replace(pathname, { locale: newLocale });
}
return (
)
}
最后一步,在Header组件中添加语言切换器,工作完成!我们的静态应用程序已经国际化了
要构建应用程序,请运行:
npm run build
静态导出将生成在out文件夹中。
通过运行以下命令测试生成的输出:
npx serve@latest out
通过Next.js的特性、App Router和next-intl库的组合,您可以创建出高性能、对SEO友好且多语言友好的Web应用程序,触达全球用户。拥抱国际化,将您的Next.js项目推向新的高度!


