Reputation: 4577
I have created a nested [locale]/
folder in app/
but it just gives a 404
See my next.config.js
const nextConfig = {
experimental: {
appDir: true,
},
i18n: {
defaultLocale: 'fr,
locales: ['fr', 'en'],
localeDetection: true
}
}
Did you find a way to support i18n with React Server components ?
EDIT:
On the beta.nextjs doc it says :
We are currently not planning to include the following features in app: Internationalization (i18n)
I have as well found an open issue about it, which does not provide any workaround yet.
Upvotes: 9
Views: 14035
Reputation: 362
[EDIT] There's a guide in Next.js documentation now.
Middleware is a solution to i18n in Next.js 13. Until there is an officially recommended way.
/app
directory, you must create the /page
directory (even if all your pages are going to be in /app
directory) and a .keep
file inside of it. Github issue about it[locale]
in the app
directory and put all other routes there.locale
param to ones that have it (f.e. based on Accept-language
header or location of the client).locale
on a page from params.A minimal middleware.ts
content inspired by Eric Howey's article:
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);
}
}
Upvotes: 8
Reputation: 3178
Even though i18n is no longer directly supported by Next.js in combination with the new app directory, there is still a way to solve this...
With the help of a middleware and with the use of i18next, react-i18next and i18next-resources-to-backend this is solvable.
It works on server side and on client side.
There's a little example showing how this could work directly by using i18next and react-i18next on server side and on client side.
...and here the corresponding blog post.
A snipped on how this could look like:
server side:
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}/>
</>
)
}
client side:
'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} />
</>
)
}
Upvotes: 0