天天看點

為你的Next.js靜态網站國際化(使用App Router)

作者:科技狠活與軟體技術

Next.js提供的一個令人驚歎的功能是靜态導出(Static Export)。啟用靜态導出後,Next.js會基于你的整個React應用程式生成靜态的HTML/CSS/JS檔案。

關注我帶你了解科技領域最新的技術與産品。

為你的Next.js靜态網站國際化(使用App Router)

這種方法有很多好處,尤其是對于性能和SEO方面。這也意味着你的應用程式可以部署和托管在任何可以提供HTML/CSS/JS靜态資源的Web伺服器上,比如一個簡單而實惠的nginx配置。

在使用靜态導出時,我遇到了一個問題,即如何進行國際化(i18n)并将我的應用程式内容翻譯成多種語言。我發現線上資源關于這個主題的文章非常少,特别是在使用Next.js 13版本引入的App Router時。

讓我們一起建立一個基本的國際化應用程式,示範如何實作這一點!

為你的Next.js靜态網站國際化(使用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 (

<div className="bg-gray-200 w-screen shadow">

<nav className="container flex px-2 py-2 gap-5 ">

<Link href="/">Home</Link>

<Link href="/about">About</Link>

</nav>

</div>

)

}

1.2 在首頁上更新為'Hello World'

// app/page.tsx

export default function Home() {

return (

<div>

Hello World!

</div>

)

}

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 (

<html lang="en">

<body className='bg-gray-100'>

<Header />

<div className='p-5'>

{children}

</div>

</body>

</html>

)

}1.4 建立關于頁面:

// app/about/page.tsx

export default function AboutPage() {

return (

<div>

這個應用是一個完全翻譯的靜态網站

</div>

)

}

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 (

<div>

自定義404頁面

</div>

)

}

建立一個新的 /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 (

<html lang="en">

<body className='bg-gray-100'>

<NextIntlClientProvider locale={locale} messages={messages}>

<Header />

<div className='p-5'>

{children}

</div>

</NextIntlClientProvider>

</body>

</html>

)

}

我們在這裡做了以下工作:

添加了一個名為getMessages的函數,用于擷取翻譯資訊。

添加了一個名為generateStaticParams的函數,用于為所有的語言環境生成靜态路由。

添加了上下文提供者NextIntlClientProvider,使得我們的翻譯能夠在應用的所有頁面中使用。

更新了頁面群組件,以使用翻譯資訊。

具體代碼如下所示:

tsx

// app/page.tsx

'use client'

import { useTranslations } from 'next-intl'

export default function HomePage() {

const t = useTranslations('HomePage')

return (

<div>

{t('helloWorld')}

</div>

)

}

tsx

// app/[locale]/about/page.tsx

'use client'

import { useTranslations } from 'next-intl'

export default function AboutPage() {

const t = useTranslations('AboutPage')

return (

<div>

{t('aboutText')}

</div>

)

}

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 (

<div className="bg-gray-200 w-screen shadow">

<nav className="container flex px-2 py-2 gap-5 ">

<Link href="/">{t('home')}</Link>

<Link href="/about">{t('about')}</Link>

</nav>

</div>

)

}

更新連結以考慮語言環境字首,使用'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 (

<div className="bg-gray-200 w-screen shadow">

<nav className="container flex px-2 py-2 gap-5 ">

<Link href="/">{t('home')}</Link>

<Link href="/about">{t('about')}</Link>

</nav>

</div>

)

}

如果你運作應用程式,你會注意到你會立即從 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<HTMLSelectElement>) => {

const newLocale = e.target.value;

router.replace(pathname, { locale: newLocale });

}

return (

<select

defaultValue={locale}

onChange={onLocaleChange}

>

{['en', 'fr'].map((lang) => (

<option key={lang} value={lang}>

{t('locale', { locale: lang })}

</option>

))}

</select>

)

}

最後一步,在Header元件中添加語言切換器,工作完成!我們的靜态應用程式已經國際化了

要建構應用程式,請運作:

npm run build

靜态導出将生成在out檔案夾中。

通過運作以下指令測試生成的輸出:

npx serve@latest out

通過Next.js的特性、App Router和next-intl庫的組合,您可以建立出高性能、對SEO友好且多語言友好的Web應用程式,觸達全球使用者。擁抱國際化,将您的Next.js項目推向新的高度!

繼續閱讀