vercel / next.js

The React Framework
https://nextjs.org
MIT License
125.96k stars 26.87k forks source link

The `app-dir-i18n-routing` example doesn't respect defaultLocale and doesn't preserve selected locale while navigating #69735

Open ivanzusko opened 1 month ago

ivanzusko commented 1 month ago

Verify canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.3.0: Thu Dec 21 02:29:41 PST 2023; root:xnu-10002.81.5~11/RELEASE_ARM64_T8122
  Available memory (MB): 8192
  Available CPU cores: 8
Binaries:
  Node: 21.7.2
  npm: 10.5.0
  Yarn: N/A
  pnpm: 9.6.0
Relevant Packages:
  next: 14.2.8 // Latest available version is detected (14.2.8).
  eslint-config-next: N/A
  react: 18.3.1
  react-dom: 18.3.1
  typescript: 4.9.5
Next.js Config:
  output: N/A

Which example does this report relate to?

app-dir-i18n-routing

What browser are you using? (if relevant)

Chrome 128.0.6613.119

How are you deploying your application? (if relevant)

No response

Describe the Bug

  1. The dafaultLocale is not being respected (setting this parameter to anything will still bring /en in the url);
  2. The locale discards back to the /en after changing it to something different(eg de) and navigating to the different page.

Expected Behavior

  1. I set defaultLocale and i can see it by default.
  2. After selecting another locale and navigating to another page - i can see selected locale in the URL.

To Reproduce

Issue 1 (defaultLocale is not being respected)

Step 1

Go to the https://github.com/vercel/next.js/blob/canary/examples/app-dir-i18n-routing/i18n-config.ts#L2 and change defaultLocale to de or cs.

Step 2

Start application.

Step 3

You will see http://localhost:3000/en instead of http://localhost:3000/de.

Issue 2 (locale isn't preserved after changing it and navigating to a different page)

Step1

Add new page under [lang] folder:

// app/[lang]/johnny/page.tsx

import { Locale } from "../../../i18n-config";
import Navigation from "../components/navigation";

export default async function JohnnyPage({
  params: { lang },
}: {
  params: { lang: Locale };
}) {
  return (
    <div>
      <Navigation />
      <div>
        <p>Current locale: {lang}</p>

      </div>
    </div>
  );
}

Step 2

Add Navigation component:

// app/[lang]/components/navigation.tsx

import Link from "next/link"

export default function Navigation () {
  return (
    <nav>
      <ul>
        <li>
          <Link href="/">home</Link>
        </li>
        <li>
          <Link href="/johnny">johnny</Link>
        </li>
      </ul>
    </nav>
  )
}

Step 3

Include Navigation to the main page:

// app/[lang]/page.tsx

import { getDictionary } from "../../get-dictionary";
import { Locale } from "../../i18n-config";
import Counter from "./components/counter";
import LocaleSwitcher from "./components/locale-switcher";
import Navigation from "./components/navigation"; // include Navigation

export default async function IndexPage({
  params: { lang },
}: {
  params: { lang: Locale };
}) {
  const dictionary = await getDictionary(lang);

  return (
    <div>
      <Navigation /> {/* this was added */}
      <LocaleSwitcher />
      <div>
        <p>Current locale: {lang}</p>
        <p>
          This text is rendered on the server:{" "}
          {dictionary["server-component"].welcome}
        </p>
        <Counter dictionary={dictionary.counter} />
      </div>
    </div>
  );
}

Step 4

Start the application

Step 5

Change locale from en to de(or cs)

Step 6

In the navigation click on johnny

Step 7

The redirection will be happen to http://localhost:3000/en/johnny (instead of http://localhost:3000/de/johnny).

R1013-T commented 1 month ago

@ivanzusko

Current Behavior As you've observed, setting the defaultLocale to "de" still results in a redirection to /en when accessing the root URL (http://localhost:3000/).

Explanation Next.js's internationalization (i18n) routing system is designed to prioritize the user's browser Accept-Language header over the defaultLocale. This means that if the browser is set to prefer English (en), Next.js will redirect to /en even if defaultLocale is set to another language like German (de).

This behavior ensures that users receive content in their preferred language whenever possible. However, we understand that there are scenarios where you might want to prioritize the defaultLocale regardless of the browser settings.


If you prefer to always redirect to the defaultLocale (e.g., "de") regardless of the user's browser settings, you can customize the middleware to achieve this behavior. Below is a sample implementation:

// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { i18n } from "./i18n-config";

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // パス名に既にサポートされているロケールが含まれているか確認
  if (i18n.locales.some(locale => pathname.startsWith(`/${locale}`))) {
    return NextResponse.next();
  }

  // デフォルトロケールにリダイレクト
  const locale = i18n.defaultLocale;
  return NextResponse.redirect(new URL(`/${locale}${pathname}`, request.url));
}

export const config = {
  // 公開アセットを除くすべてのルートにミドルウェアを適用
  matcher: '/:path*',
};
ivanzusko commented 1 month ago

@R1013-T

This behavior ensures that users receive content in their preferred language whenever possible. However, we understand that there are scenarios where you might want to prioritize the defaultLocale regardless of the browser settings.

I would say on one hand it makes total sense 👍
But at the same time it isn't clear from the very beginning. I guess it could be 2 types of behaviour:

  1. respect the users browser Accept-Language header by default and if there is nothing - to show the defaultLocale.
  2. to make extra settings flag which would enforce prioritization of defaultLocale over the browser header.

But if the 1st issue can be somehow reasoned, the second issue with not respecting user selection is a bit more critical I would say. If the user explicitly changes the locale, it must be respected and preserved.