nuxt-modules / i18n

I18n module for Nuxt
https://i18n.nuxtjs.org
MIT License
1.74k stars 481 forks source link

experimental.localeDetector and useTranslation fails to load i18n config on initial request #2721

Closed fakedob closed 8 months ago

fakedob commented 9 months ago

Environment

Reproduction

Wont be able to provide reproduction, as in reality, it will require access to the host in serverless environment

Describe the bug

I've setup SSR translations with

i18n: {
    experimental: {
      localeDetector: './localeDetector.ts'
    }

In my event handler, I call useTranslation to access the t function, as follows:

export default defineEventHandler(async (ctx, next) => {
  const t = await useTranslation(ctx);
})

All this is working as expected when run in development. In our cloud, we use serverless framework, which scales our application. There is a "cold start" procedure, which runs when the app was either not used for some time, or needs to start a new instance. That is the reason why I am able to hit the issue, as on other build types (or dev), the module loads, prior making the first request directly into the handler, that uses the t function.

If, when the app was called directly on the event handler that uses the useTranslations, the nuxtConfig for i18n is not yet available in the context, thus, resulting in "middleware not initialized, please setup onRequest and onAfterResponse options of createApp with the middleware obtained with defineI18nMiddleware". It also worth mentioning, that there is a period of 1-3 seconds between the initial app load and when the config is available in the context. My best bet is, that the nuxt/i18n module is preparing the config in async, before appending it to the context of the request.

So far I am out of clue, why is this happening. Seems like the event handler gets called, prior the i18n was properly loaded into context.

Additional context

No response

Logs

No response

github-actions[bot] commented 9 months ago

Would you be able to provide a reproduction? πŸ™

More info ### Why do I need to provide a reproduction? Reproductions make it possible for us to triage and fix issues quickly with a relatively small team. It helps us discover the source of the problem, and also can reveal assumptions you or we might be making. ### What will happen? If you've provided a reproduction, we'll remove the label and try to reproduce the issue. If we can, we'll mark it as a bug and prioritise it based on its severity and how many people we think it might affect. If `needs reproduction` labeled issues don't receive any substantial activity (e.g., new comments featuring a reproduction link), we'll close them. That's not because we don't care! At any point, feel free to comment with a reproduction and we'll reopen it. ### How can I create a reproduction? We have a couple of templates for starting with a minimal reproduction: πŸ‘‰ [Reproduction starter (v8 and higher)](https://stackblitz.com/fork/github/BobbieGoede/nuxt-i18n-starter/tree/v8) πŸ‘‰ [Reproduction starter (edge)](https://stackblitz.com/fork/github/BobbieGoede/nuxt-i18n-starter/tree/edge) A public GitHub repository is also perfect. πŸ‘Œ Please ensure that the reproduction is as **minimal** as possible. See more details [in our guide](https://nuxt.com/docs/community/reporting-bugs/#create-a-minimal-reproduction). You might also find these other articles interesting and/or helpful: - [The Importance of Reproductions](https://antfu.me/posts/why-reproductions-are-required) - [How to Generate a Minimal, Complete, and Verifiable Example](https://stackoverflow.com/help/mcve)
plushdohn commented 9 months ago

Running into this on a Vercel deployment. First request fails, following requests go through. It's a private repo so sorry for not being able to offer a reproduction yet, I'll see if I can come up with one.

BobbieGoede commented 8 months ago

Closing this due to inactivity and lack of reproduction, if you're still experiencing this issue please open a new issue with a (minimal) reproduction.

I understand that it's not possible to share private code, or that it's difficult to replicate due to the environment, but for us to be able to debug this issue we would have to put in the time and effort to figure out how to replicate your issue by ourselves.

KyleXie commented 8 months ago

Hey, I'm having the same issue here. My app runs on a serverless environment, and this issue happens a lot on "cold start". It works fine on the coming requests when the instance warms up. I'm using @nuxtjs/i18n@8.0.0.

AntonGrafton commented 4 months ago

Same here

bart commented 4 months ago

Unfortunately same issue here, also in dev mode

fakedob commented 4 months ago

Unfortunately same issue here, also in dev mode

Hey @bart, if it happens in Dev, can you share a small reproduction? Here we all experience this issue only on prod, which makes it impossible to give feedback on the real cause. If this can be tested in dev, then we can see where the error comes from ✌️

bart commented 4 months ago

Sure @fakedob I'm also diving into it already. useTranslation() is checking for event.context.i18n and if this is null it is throwing the error. Here is a reproduction link: https://stackblitz.com/~/github.com/bart/i18n-repro

bart commented 4 months ago

@fakedob Did you have the chance to look into it already?

bart commented 4 months ago

All right!! After digging deep into the module I figured out what the reason for this issue was. Module checks for enableServerIntegration to load server-side middleware that registers i18n there. Problem: When configuring i18n inside of the modules key it doesn't work because experimental gets loaded as undefined. When using the i18n key instead for configuration, it works. Maybe that's also interesting for others who are facing the same issue.

// nuxt.confg.js - not working
export default defineNuxtConfig({
  compatibilityDate: '2024-04-03',
  devtools: { enabled: true },
  modules: [["@nuxtjs/i18n", {
    experimental: {
      localeDetector: "./localeDetector.ts",
    },
    defaultLocale: "en",
    locales: [
      {
        code: "en",
        name: "English",
        file: "en.yaml",
      },
    ],
    langDir: "locales",
  }]]
})

// nuxt.config.js - working
export default defineNuxtConfig({
  compatibilityDate: '2024-04-03',
  devtools: { enabled: true },
  modules: ["@nuxtjs/i18n"],
  i18n: {
    experimental: {
      localeDetector: "./localeDetector.ts",
    },
    defaultLocale: "en",
    locales: [
      {
        code: "en",
        name: "English",
        file: "en.yaml",
      },
    ],
    langDir: "locales",
  }
})
BobbieGoede commented 4 months ago

@bart Thanks for investigating into the source of the issue! This should get resolved by #3020.

fakedob commented 3 months ago

All right!! After digging deep into the module I figured out what the reason for this issue was. Module checks for enableServerIntegration to load server-side middleware that registers i18n there. Problem: When configuring i18n inside of the modules key it doesn't work because experimental gets loaded as undefined. When using the i18n key instead for configuration, it works. Maybe that's also interesting for others who are facing the same issue.

// nuxt.confg.js - not working
export default defineNuxtConfig({
  compatibilityDate: '2024-04-03',
  devtools: { enabled: true },
  modules: [["@nuxtjs/i18n", {
    experimental: {
      localeDetector: "./localeDetector.ts",
    },
    defaultLocale: "en",
    locales: [
      {
        code: "en",
        name: "English",
        file: "en.yaml",
      },
    ],
    langDir: "locales",
  }]]
})

// nuxt.config.js - working
export default defineNuxtConfig({
  compatibilityDate: '2024-04-03',
  devtools: { enabled: true },
  modules: ["@nuxtjs/i18n"],
  i18n: {
    experimental: {
      localeDetector: "./localeDetector.ts",
    },
    defaultLocale: "en",
    locales: [
      {
        code: "en",
        name: "English",
        file: "en.yaml",
      },
    ],
    langDir: "locales",
  }
})

Hey @bart , thank you for your contribution and sorry for my late reply, I was on holiday. Unfortunately, for me it doesnt solve the issue and the key in my nuxt.config is correct. As far as I can understand, the i18n config gets set in an async way, that doesnt wait for the configuration to be setup, prior executing the first response. Based on my experiments, in few ms after the first call, its already set and every next works normally. On my end, this cannot be replicated in normal dev or host environment, it must be serverless host, that has a cold start, which means, the app must be build specifically for this type of hosting and executed in a cold start, to be able to reproduce it. Without knowing more of the internals of how this module works, I cannot say more at this point, I did not had any chance to look deeper into the code, but I am sure it is arround the setup of the i18n initial config being done in async without being awaited.

nstdspace commented 2 months ago

I just tried to setup server side translations following the documentation on version 8.4.0 - the same error appears:

middleware not initialized, please setup onRequest and onAfterResponse options of createApp with the middleware obtained with defineI18nMiddleware and I have the config as @bart suggests.

Why is this issue closed?

BobbieGoede commented 2 months ago

@nstdspace Can you open a new issue with a minimal reproduction? πŸ™

fakedob commented 2 months ago

@nstdspace

Can you open a new issue with a minimal reproduction? πŸ™

Hey @BobbieGoede as in my initial post, I tried to explain the problem does not come from client code. It can only be reproduced in environment that can do cold start as the serveless framework does. If you want, i think i can make you a serverless npm project for you to run and test, but for the i18n module, its basically the initial config as in documentation is enough to reproduce.

My best guess is that the config in the nuxt.config gets loaded async and thats why the initial request fails, as the condig has not been set up.

You cannot reproduce this in dev environment, since you run a dev server and when you hit the api, the config has already been loaded.

In serverless, your server is dead until someone request something from it and the process of the server is being handled differently. Thats why we experience the problem only when deployed on such infrastructure.

BobbieGoede commented 2 months ago

@fakedob If you can create a minimal reproduction with instructions on how to run it in a serverless environment (because I haven't done this before), or are able to reproduce the issue by replicating the cold start somehow, that would allow me to investigate the issue further.

From what I can tell the Nuxt config is not loaded asynchronously so I wonder what else it could be πŸ€”