aralroca / next-translate

Next.js plugin + i18n API for Next.js 🌍 - Load page translations and use them in an easy way!
MIT License
2.64k stars 207 forks source link

homepage 404 error, when using nextjs13.4 version #1111

Open blackrabbit944 opened 1 year ago

blackrabbit944 commented 1 year ago

What version of this package are you using?

  "dependencies": {
    "@types/node": "20.4.2",
    "@types/react": "18.2.15",
    "@types/react-dom": "18.2.7",
    "autoprefixer": "10.4.14",
    "eslint": "8.45.0",
    "eslint-config-next": "13.4.10",
    "next": "13.4.10",
    "next-translate": "^2.5.0",
    "postcss": "8.4.26",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "tailwindcss": "3.3.3",
    "typescript": "5.1.6"
  },
  "devDependencies": {
    "next-translate-plugin": "^2.5.0"
  }

What operating system, Node.js, and npm version? npm : 8.5.1 nodejs: v18.16.0

What happened?

After using npx create-next-app@latest to create a project and only adding the next-translate library, the home page cannot be accessed and a 404 error is displayed.

Whether it is /ja /en /zh or / cannot be accessed, a 404 error is displayed

What did you expect to happen?

I hope the normal home page can be displayed

Are you willing to submit a pull request to fix this bug?

I don't think I can do it


with i18n.js and next.config.js

i18n.js


module.exports = {
    locales: ["en", "zh", "ja"],
    defaultLocale: "en",
    pages: {
        "*": ["common"],
    },
    loadLocaleFrom: (lang, ns) =>
        import(`./public/locales/${lang}/${ns}.json`).then((m) => m.default),
};

next.config.js

const nextConfig = {
    experimental: {
        appDir: true,
    },
};
const nextTranslate = require("next-translate-plugin");

module.exports = nextTranslate(nextConfig);

whatever i add experimental.appDir or remove it still get 404 error

and with the nextjs config

✔ What is your project named? … testapp
✔ Would you like to use TypeScript? … No / **Yes**
✔ Would you like to use ESLint? … No / **Yes**
✔ Would you like to use Tailwind CSS? … No / **Yes**
✔ Would you like to use `src/` directory? … No / Yes //i tried both of this still get 404
✔ Would you like to use App Router? (recommended) … No / **Yes**
✔ Would you like to customize the default import alias? … **No** / Yes
darr1s commented 1 year ago

Same here. Latest example only works if I am already in e.g. localhost:3000/en/news. If I go to localhost:3000 it will throw 404.

damienmutt commented 1 year ago

In my case must declare explicitly a locale in the URL to view the homepage, if no locale is provided it shows a 404. http://localhost:3001/ -> don't work http://localhost:3001/en-US -> work

Using app router with: "next": "^13.4.9", "next-translate": "^2.5.2", "next-translate-plugin": "^2.5.2",

Any way to have homepage working on default language?

aralroca commented 1 year ago

We are investigating a better way to fix that, because now i18n has some trobles with the middleware (issue https://github.com/vercel/next.js/issues/49883), but for now can be solved adding a app/route.(js|ts) file:

import { NextRequest } from 'next/server';
import { redirect } from 'next/navigation';
import i18nConfig from '../../i18n';

export async function GET(request: NextRequest) {
  const userPreferredLanguage =
    request.headers.get('accept-language')?.split(',')?.[0] ??
    i18nConfig.defaultLocale;

  const lang = i18nConfig.locales.includes(userPreferredLanguage)
    ? userPreferredLanguage
    : i18nConfig.defaultLocale;

  return redirect(`/${lang}${request.nextUrl.pathname.toLowerCase()}`);
}

src/app
 ├── [lang]
 │   │── layout.tsx
 │   ├── loading.tsx
 │   ├── page.tsx
 └── route.ts
ygorp2 commented 1 year ago

I have the same problem using the regular Next Pages, any way to solve it? Doesn't happen on localhost, but it does on Vercel, if I click on any link on the page doesn't happen and only occurs on the home page.

next 13.4.4 next-translate 2.4.4 next-translate-plugin 2.4.4

aralroca commented 1 year ago

I have the same problem using the regular Next Pages, any way to solve it? Doesn't happen on localhost, but it does on Vercel, if I click on any link on the page doesn't happen and only occurs on the home page.

next 13.4.4 next-translate 2.4.4 next-translate-plugin 2.4.4

I think only happens when accept-language is not comming. By default we are adding the i18n in the next.config.js config and is doing this redirect to the prefered language of the user. However when the user doesn't have a preferred language or accept-language header is not comming, then is displaying this 404. In order to solve it is better for now to implement the solution I proposed above.

ygorp2 commented 1 year ago

How can I achieve that in the pages router?

aralroca commented 1 year ago

How can I achieve that in the pages router?

This problem is not related with pages router. With pages router there aren't routing problems because all the i18n routing part is implemented in the core of Next.js (what next-translate uses under the hood): https://nextjs.org/docs/pages/building-your-application/routing/internationalization

ygorp2 commented 1 year ago

How can I achieve that in the pages router?

This problem is not related with pages router. With pages router there aren't routing problems because all the i18n routing part is implemented in the core of Next.js (what next-translate uses under the hood): nextjs.org/docs/pages/building-your-application/routing/internationalization

Should I open another issue then?

I'm getting a production error on Vercel using the pages router. Only happens on the homepage and if the person is using one of my locales specified in the i18n.json without being the default one, doesn't happen on localhost or if the person uses a link in my site that points to the home.

Live site: https://spotify-api-challenge.vercel.app/pt-BR

Repo: https://github.com/ygorp2/spotify-api-challenge/tree/without-oauth

Aramayis331 commented 1 year ago

I have the same problem using the App Router, homepage 404 error.

next 13.4.12 next-translate 2.5.2 next-translate-plugin 2.5.2

damienmutt commented 1 year ago

We are investigating a better way to fix that, because now i18n has some trobles with the middleware (issue vercel/next.js#49883), but for now can be solved adding a app/route.(js|ts) file:

import { NextRequest } from 'next/server';
import { redirect } from 'next/navigation';
import i18nConfig from '../../i18n';

export async function GET(request: NextRequest) {
  const userPreferredLanguage =
    request.headers.get('accept-language')?.split(',')?.[0] ??
    i18nConfig.defaultLocale;

  const lang = i18nConfig.locales.includes(userPreferredLanguage)
    ? userPreferredLanguage
    : i18nConfig.defaultLocale;

  return redirect(`/${lang}${request.nextUrl.pathname.toLowerCase()}`);
}
src/app
 ├── [lang]
 │   │── layout.tsx
 │   ├── loading.tsx
 │   ├── page.tsx
 └── route.ts

Hi again, I found that this workaround works when server is already up but once you shut down and start server again it throws ERR_TOO_MANY_REDIRECTS because of redirect.

RobSchilderr commented 1 year ago

I have the same problem using the regular Next Pages, any way to solve it? Doesn't happen on localhost, but it does on Vercel, if I click on any link on the page doesn't happen and only occurs on the home page. next 13.4.4 next-translate 2.4.4 next-translate-plugin 2.4.4

I think only happens when accept-language is not comming. By default we are adding the i18n in the next.config.js config and is doing this redirect to the prefered language of the user. However when the user doesn't have a preferred language or accept-language header is not comming, then is displaying this 404. In order to solve it is better for now to implement the solution I proposed above.

I am still getting this error in production at my homepage in the /app router, when I go to /en-US:

Error: Invariant: The detected locale does not match the locale in the query. Expected to find 'nl-NL' in '/en-US' but found 'en-US'}

When I go to nl-NL as default, it works fine.

With route.ts:

import { redirect } from 'next/navigation';
import i18nConfig from  '../../i18n.json'

export async function GET(request: NextRequest) {
  const userPreferredLanguage =
    request.headers.get('accept-language')?.split(',')?.[0] ??
    i18nConfig.defaultLocale;

  const lang = i18nConfig.locales.includes(userPreferredLanguage)
    ? userPreferredLanguage
    : i18nConfig.defaultLocale;

  return redirect(`/${lang}${request.nextUrl.pathname.toLowerCase()}`);
}

With i18n.json:

  "locales": ["nl-NL", "en-US", "tr-TR", "pl-PL"],
  "defaultLocale": "nl-NL",
  "pages": {
    "*": ["common", "home"],
    "/[lang]": ["pricing", "faq", "faqFreelancer", "freelancer", "search"]
  }
}

And in the RootLayout:

  // Layouts must accept a children prop.
  // This will be populated with nested layouts or pages
  children,
}: {
  children: React.ReactNode
}) {

  const {  lang } = useTranslation('common')

  if (!i18n.locales.includes(lang)) redirect(`/${i18n.defaultLocale}/${lang}`)

  return (
    <html lang={lang} suppressHydrationWarning>
      <body
      className={cn(
        "min-h-screen antialiased",
        fontSans.variable,
       )}>
            <Header variant="default" />

        <main>{children}
        </main>
        <Footer/> 
      </body>
    </html>
  )
}

Am I forgetting something?

versions:

next 13.4.9
next-translate 2.5.3
next-translate-plugin 2.5.3
RobSchilderr commented 1 year ago

How can I achieve that in the pages router?

This problem is not related with pages router. With pages router there aren't routing problems because all the i18n routing part is implemented in the core of Next.js (what next-translate uses under the hood): nextjs.org/docs/pages/building-your-application/routing/internationalization

Should I open another issue then?

I'm getting a production error on Vercel using the pages router. Only happens on the homepage and if the person is using one of my locales specified in the i18n.json without being the default one, doesn't happen on localhost or if the person uses a link in my site that points to the home.

Live site: https://spotify-api-challenge.vercel.app/pt-BR

Repo: https://github.com/ygorp2/spotify-api-challenge/tree/without-oauth

Did you solve this issue? I have it in /app

YgorPerez commented 1 year ago

Did you solve this issue? I have it in /app

No, it still gives a 404 unluckily.

Abramovick commented 1 year ago

I did solve it with what @aralroca said but with these specific versions of next and next-translate. I tried to bump up next and next-translate and noticed his solution stopped working, so i can say it deffo works on these versions.

"next": "13.4.12",
"next-translate": "^2.5.2",
"next-translate-plugin": "^2.5.2",
RobSchilderr commented 1 year ago

I did solve it with what @aralroca said but with these specific versions of next and next-translate. I tried to bump up next and next-translate and noticed his solution stopped working, so i can say it deffo works on these versions.

"next": "13.4.12",
"next-translate": "^2.5.2",
"next-translate-plugin": "^2.5.2",

For me even with this versions, I keep getting:

Error: Invariant: The detected locale does not match the locale in the query. Expected to find 'nl-NL

Abramovick commented 1 year ago

I did solve it with what @aralroca said but with these specific versions of next and next-translate. I tried to bump up next and next-translate and noticed his solution stopped working, so i can say it deffo works on these versions.

"next": "13.4.12",
"next-translate": "^2.5.2",
"next-translate-plugin": "^2.5.2",

For me even with this versions, I keep getting:

Error: Invariant: The detected locale does not match the locale in the query. Expected to find 'nl-NL

In my personal use case i saw that there's sooooo many variants of english and i don't really care about the minor differences between spelling for en-GB or en-US, and the rest en-029, en-AU, en-BZ, en-CA, en-cb, en-IE, en-IN and etc, so because in MY USE CASE i don't care about the variants i adjusted it to ignore the last part after the - and only detect the first en.

route.ts

import { NextRequest } from 'next/server';
import { redirect } from 'next/navigation';

import i18nConfig from '../../i18n';

export async function GET(request: NextRequest) {
  let userPreferredLanguage =
    request.headers.get('accept-language')?.split(',')?.[0] ??
    i18nConfig.defaultLocale;

  const regex = /en-[A-Z]{2}/g;
  const englishVariants = userPreferredLanguage.match(regex);
  if (englishVariants)
    userPreferredLanguage = userPreferredLanguage.replace(
      englishVariants[0],
      'en',
    );

  const lang = i18nConfig.locales.includes(userPreferredLanguage)
    ? userPreferredLanguage
    : i18nConfig.defaultLocale;

  return redirect(`/${lang}${request.nextUrl.pathname.toLowerCase()}`);
}

i18n.js

const i18n = {
  locales: ['en', 'sw'],
  defaultLocale: 'en',
  pages: {
    '*': ['common'],
    '/[lang]': ['home'],
    '/[lang]/search': ['search'],
    '/[lang]/ticket': ['ticket'],
    '/[lang]/ticket/review': ['ticket'],
  },
};

module.exports = i18n;

looking at your error Error: Invariant: The detected locale does not match the locale in the query. Expected to find 'nl-NL, I suggest you log and see what locale comes. console.log this request.headers.get('accept-language')?.split(',')?.[0] so you can know what comes in the locale. If nothing comes then you have another issue

RobSchilderr commented 1 year ago

I did solve it with what @aralroca said but with these specific versions of next and next-translate. I tried to bump up next and next-translate and noticed his solution stopped working, so i can say it deffo works on these versions.

"next": "13.4.12",
"next-translate": "^2.5.2",
"next-translate-plugin": "^2.5.2",

For me even with this versions, I keep getting: Error: Invariant: The detected locale does not match the locale in the query. Expected to find 'nl-NL

In my personal use case i saw that there's sooooo many variants of english and i don't really care about the minor differences between spelling for en-GB or en-US, and the rest en-029, en-AU, en-BZ, en-CA, en-cb, en-IE, en-IN and etc, so because in MY USE CASE i don't care about the variants i adjusted it to ignore the last part after the - and only detect the first en.

route.ts

import { NextRequest } from 'next/server';
import { redirect } from 'next/navigation';

import i18nConfig from '../../i18n';

export async function GET(request: NextRequest) {
  let userPreferredLanguage =
    request.headers.get('accept-language')?.split(',')?.[0] ??
    i18nConfig.defaultLocale;

  const regex = /en-[A-Z]{2}/g;
  const englishVariants = userPreferredLanguage.match(regex);
  if (englishVariants)
    userPreferredLanguage = userPreferredLanguage.replace(
      englishVariants[0],
      'en',
    );

  const lang = i18nConfig.locales.includes(userPreferredLanguage)
    ? userPreferredLanguage
    : i18nConfig.defaultLocale;

  return redirect(`/${lang}${request.nextUrl.pathname.toLowerCase()}`);
}

i18n.js

const i18n = {
  locales: ['en', 'sw'],
  defaultLocale: 'en',
  pages: {
    '*': ['common'],
    '/[lang]': ['home'],
    '/[lang]/search': ['search'],
    '/[lang]/ticket': ['ticket'],
    '/[lang]/ticket/review': ['ticket'],
  },
};

module.exports = i18n;

looking at your error Error: Invariant: The detected locale does not match the locale in the query. Expected to find 'nl-NL, I suggest you log and see what locale comes. console.log this request.headers.get('accept-language')?.split(',')?.[0] so you can know what comes in the locale. If nothing comes then you have another issue

I changed the configuration to match exactly what you provided, but I still encounter the same error. When I set the defaultLocale to 'en', it continuously redirects me from /en to /en/ or vice versa.

The console log, as you pointed out, indeed returns undefined.

Interestingly, this problem is unique to the next-translate package. I tried switching to next-intl and didn't experience any issues. However, transitioning completely to next-intl would take several days. I really like the next-translate package, so I hope I can find a solution to this issue.

Abramovick commented 1 year ago

if you're getting undefined when you log request.headers.get('accept-language')?.split(',')?.[0] in your route.ts then try doing this on your layout.tsx

export default function RootLayout({
  children,
  params: { lang },
}: {
  children: React.ReactNode;
  params: { lang: string };
}) {
  // Redirect to default locale if lang is not supported.
  if (!i18n.locales.includes(lang)) redirect(`/${i18n.defaultLocale}/`);   // <-------- ADD THIS LINE HERE IS KEY

  return (
    <html lang={lang}> // <---------------- ADD THIS LINE HERE IS KEY
      <body className={`${manrope.variable}`}>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}
RobSchilderr commented 1 year ago

if you're getting undefined when you log request.headers.get('accept-language')?.split(',')?.[0] in your route.ts then try doing this on your layout.tsx

export default function RootLayout({
  children,
  params: { lang },
}: {
  children: React.ReactNode;
  params: { lang: string };
}) {
  // Redirect to default locale if lang is not supported.
  if (!i18n.locales.includes(lang)) redirect(`/${i18n.defaultLocale}/`);   // <-------- ADD THIS LINE HERE IS KEY

  return (
    <html lang={lang}> // <---------------- ADD THIS LINE HERE IS KEY
      <body className={`${manrope.variable}`}>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

if you're getting undefined when you log request.headers.get('accept-language')?.split(',')?.[0] in your route.ts then try doing this on your layout.tsx

export default function RootLayout({
  children,
  params: { lang },
}: {
  children: React.ReactNode;
  params: { lang: string };
}) {
  // Redirect to default locale if lang is not supported.
  if (!i18n.locales.includes(lang)) redirect(`/${i18n.defaultLocale}/`);   // <-------- ADD THIS LINE HERE IS KEY

  return (
    <html lang={lang}> // <---------------- ADD THIS LINE HERE IS KEY
      <body className={`${manrope.variable}`}>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Even with that I am getting a 500 internal server error:


Error: Invariant: The detected locale does not match the locale in the query. Expected to find 'nl' in '/en' but found 'en'}
    at I18NProvider.fromQuery (/var/task/node_modules/next/dist/server/future/helpers/i18n-provider.js:70:27)```
Abramovick commented 1 year ago

if you're getting undefined when you log request.headers.get('accept-language')?.split(',')?.[0] in your route.ts then try doing this on your layout.tsx

export default function RootLayout({
  children,
  params: { lang },
}: {
  children: React.ReactNode;
  params: { lang: string };
}) {
  // Redirect to default locale if lang is not supported.
  if (!i18n.locales.includes(lang)) redirect(`/${i18n.defaultLocale}/`);   // <-------- ADD THIS LINE HERE IS KEY

  return (
    <html lang={lang}> // <---------------- ADD THIS LINE HERE IS KEY
      <body className={`${manrope.variable}`}>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

if you're getting undefined when you log request.headers.get('accept-language')?.split(',')?.[0] in your route.ts then try doing this on your layout.tsx

export default function RootLayout({
  children,
  params: { lang },
}: {
  children: React.ReactNode;
  params: { lang: string };
}) {
  // Redirect to default locale if lang is not supported.
  if (!i18n.locales.includes(lang)) redirect(`/${i18n.defaultLocale}/`);   // <-------- ADD THIS LINE HERE IS KEY

  return (
    <html lang={lang}> // <---------------- ADD THIS LINE HERE IS KEY
      <body className={`${manrope.variable}`}>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Even with that I am getting a 500 internal server error:

Error: Invariant: The detected locale does not match the locale in the query. Expected to find 'nl' in '/en' but found 'en'}
    at I18NProvider.fromQuery (/var/task/node_modules/next/dist/server/future/helpers/i18n-provider.js:70:27)```

hit me up at abraham@itule.me then we'll jump on a call, i'll help you fix it.

RobSchilderr commented 1 year ago

if you're getting undefined when you log request.headers.get('accept-language')?.split(',')?.[0] in your route.ts then try doing this on your layout.tsx

export default function RootLayout({
  children,
  params: { lang },
}: {
  children: React.ReactNode;
  params: { lang: string };
}) {
  // Redirect to default locale if lang is not supported.
  if (!i18n.locales.includes(lang)) redirect(`/${i18n.defaultLocale}/`);   // <-------- ADD THIS LINE HERE IS KEY

  return (
    <html lang={lang}> // <---------------- ADD THIS LINE HERE IS KEY
      <body className={`${manrope.variable}`}>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

if you're getting undefined when you log request.headers.get('accept-language')?.split(',')?.[0] in your route.ts then try doing this on your layout.tsx

export default function RootLayout({
  children,
  params: { lang },
}: {
  children: React.ReactNode;
  params: { lang: string };
}) {
  // Redirect to default locale if lang is not supported.
  if (!i18n.locales.includes(lang)) redirect(`/${i18n.defaultLocale}/`);   // <-------- ADD THIS LINE HERE IS KEY

  return (
    <html lang={lang}> // <---------------- ADD THIS LINE HERE IS KEY
      <body className={`${manrope.variable}`}>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Even with that I am getting a 500 internal server error:

Error: Invariant: The detected locale does not match the locale in the query. Expected to find 'nl' in '/en' but found 'en'}
    at I18NProvider.fromQuery (/var/task/node_modules/next/dist/server/future/helpers/i18n-provider.js:70:27)```

hit me up at abraham@itule.me then we'll jump on a call, i'll help you fix it.

Hey, thanks for the offer but I switched to next-intl!

b-vetter commented 1 year ago

Same problem here. Is it a nextjs bug? Know anybody when it will be fixed?

cglacet commented 9 months ago

Any idea what is that error? I have a build that works locally but fails on Vercel with that exact same error.

I tried on both node 20.x and 18.x without success.

My Next version is 13.4.7 (I also tried up to 13.4.9).

I used the strategy proposed here: https://www.izoukhai.com/blog/implement-internationalization-i18n-in-next-13-actually-working

The error I get is:

Error: Invariant: The detected locale does not match the locale in the query. Expected to find 'default' in '/fr' but found 'fr'}
    at I18NProvider.fromQuery (/var/task/node_modules/next/dist/server/future/helpers/i18n-provider.js:70:27)
    at Object.fn (/var/task/node_modules/next/dist/server/next-server.js:973:106)
    at Router.execute (/var/task/node_modules/next/dist/server/router.js:315:44)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async NextNodeServer.runImpl (/var/task/node_modules/next/dist/server/base-server.js:615:29)
    at async NextNodeServer.handleRequestImpl (/var/task/node_modules/next/dist/server/base-server.js:546:20)

I tried to look at I18NProvider.fromQuery and it seems there is something not right here because the code looks like this:

fromQuery(pathname, query) {
        const detectedLocale = query.__nextLocale;
        // If a locale was detected on the query, analyze the pathname to ensure
        // that the locale matches.
        if (detectedLocale) {
            const analysis = this.analyze(pathname);
            // If the analysis contained a locale we should validate it against the
            // query and strip it from the pathname.
            if (analysis.detectedLocale) {
                if (analysis.detectedLocale !== detectedLocale) {
                    throw new Error(`Invariant: The detected locale does not match the locale in the query. Expected to find '${detectedLocale}' in '${pathname}' but found '${analysis.detectedLocale}'}`);
                }
                pathname = analysis.pathname;
            }
        }
        return {
            pathname,
            detectedLocale,
            inferredFromDefault: query.__nextInferredLocaleFromDefault === "1"
        };
    }

So this error shouldn't even happen because I added the localeDetection: false to my i18n configuration.

If we translate the error:

throw new Error(`Invariant: The detected locale does not match the locale in the query. Expected to find '${detectedLocale}' in '${pathname}' but found '${analysis.detectedLocale}'}`);

I have:

It seems the issue is with the detectedLocale because this happens after the redirection to /fr. I have no clue what is going on, and its hard to debug because I can't reproduce this in local dev.

The requested URL [GET] /fr?nxtPlang=fr status=500 seems strange, I have no idea what nxtPlang param is:

aralroca commented 8 months ago

Can you tried in 3.0.0-canary.1 version? (both, next-translate & next-translate-plugin). Thanks