如何让i18n在Next 13上工作
我在app/
中创建了一个嵌套的[locale]/
文件夹,但它只提供了一个404
请参阅我的next.config.js
const nextConfig = {
experimental: {
appDir: true,
},
i18n: {
defaultLocale: 'fr,
locales: ['fr', 'en'],
localeDetection: true
}
}
您是否找到了使用React Server组件支持i18n的方法?
编辑:
在beta.nextjs文档上,它说:
我们目前不打算在应用程序中包含以下功能:国际化(i18n(
我也发现了一个悬而未决的问题,它还没有提供任何解决方法。
[EDIT]Next.js文档中现在有一个指南。
中间件是Next.js 13中i18n的解决方案。直到有一个官方推荐的方式。
- 要在Next.js 13中使用带有
/app
目录的中间件,您必须创建/page
目录(即使您的所有页面都将在/app
目录中(和其中的.keep
文件 - 在
app
目录中创建一个动态路由[locale]
,并将所有其他路由放在那里 - 设置中间件,使其将所有流量从没有
locale
参数的路由重定向到有参数的路由(例如,基于Accept-language
标头或客户端位置( - 从params中获取页面上
locale
的值
受Eric Howey文章启发的最小middleware.ts
内容:
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
// Regex to check whether something has an extension, e.g. .jpg
const PUBLIC_FILE = /.(.*)$/;
export function middleware(request: NextRequest) {
const { nextUrl, headers } = request;
// Cloned url to work with
const url = nextUrl.clone();
// Client language, defaults to en
const language =
headers
.get("accept-language")
?.split(",")?.[0]
.split("-")?.[0]
.toLowerCase() || "en";
try {
// Early return if it is a public file such as an image or an api call
if (PUBLIC_FILE.test(nextUrl.pathname) || nextUrl.pathname.includes("/api")) {
return undefined;
}
// Proceed without redirection if on a localized path
if (
nextUrl.pathname.startsWith("/en") ||
nextUrl.pathname.startsWith("/de") ||
nextUrl.pathname.startsWith("/fr")
) {
return undefined;
}
if (language === "fr") {
url.pathname = `/fr${nextUrl.pathname}`;
return NextResponse.redirect(url);
}
if (language === "de") {
url.pathname = `/de${nextUrl.pathname}`;
return NextResponse.redirect(url);
}
if (!["de", "fr"].includes(language)) {
url.pathname = `/en${nextUrl.pathname}`;
return NextResponse.redirect(url);
}
return undefined;
} catch (error) {
console.log(error);
}
}
即使Next.js不再直接支持i18n与新的应用程序目录相结合,仍然有办法解决这个问题。。。
在中间件的帮助下,通过使用i18next、react-i18next和i18next资源来后端,这是可以解决的。
它适用于服务器端和客户端。
有一个小例子展示了如何通过在服务器端和客户端使用i18next和react-i18next来直接工作。
这里是相应的博客文章。
关于这可能是什么样子的剪报:
服务器端:
import Link from 'next/link'
import { useTranslation } from '../i18n'
import { Footer } from './components/Footer'
export default async function Page({ params: { lng } }) {
const { t } = await useTranslation(lng)
return (
<>
<h1>{t('title')}</h1>
<Link href={`/${lng}/second-page`}>
{t('to-second-page')}
</Link>
<br />
<Link href={`/${lng}/client-page`}>
{t('to-client-page')}
</Link>
<Footer lng={lng}/>
</>
)
}
客户端:
'use client'
import Link from 'next/link'
import { useTranslation } from '../../i18n/client'
import { Footer } from '../components/Footer/client'
import { useState } from 'react'
export default function Page({ params: { lng } }) {
const { t } = useTranslation(lng, 'client-page')
const [counter, setCounter] = useState(0)
return (
<>
<h1>{t('title')}</h1>
<p>{t('counter', { count: counter })}</p>
<div>
<button onClick={() => setCounter(Math.max(0, counter - 1))}>-</button>
<button onClick={() => setCounter(Math.min(10, counter + 1))}>+</button>
</div>
<Link href={`/${lng}`}>
<button type="button">
{t('back-to-home')}
</button>
</Link>
<Footer lng={lng} />
</>
)
}