Hydration error with i18n

We're facing hydration errors with nuxt-i18n, because the locale isn't set "fast enough" in a production build for our date formatting utilities. We use dayjs and we have a watcher for the locale switching in App.vue:
const { locale } = useI18n()
watch(locale, (newLocale) => {
dates.locale(newLocale)
}, {
immediate: true,
})
const { locale } = useI18n()
watch(locale, (newLocale) => {
dates.locale(newLocale)
}, {
immediate: true,
})
Unfortunately this seems to be too slow in a prod build. Probably because Vue resolves App.vue last, not first. I tried setting it in a plugin, but nuxt-i18n throws an error when the composable is used in a pluing (bug?). Not sure how to proceed, this seems like a pretty common thing to do?
16 Replies
kapa.ai
kapa.ai3w ago
Hi @Reinier Kaper. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- This is indeed a common issue when working with i18n and date formatting in Nuxt 3. Based on the knowledge sources provided, I can offer some insights and potential solutions to your hydration error problem. The root cause of your issue seems to be related to the timing of when the locale is set and when the date formatting occurs. As you've correctly identified, the watcher in App.vue might be resolving too late in the production build process. Here are a few approaches you could consider: 1. Move the locale setting logic to a plugin: You mentioned trying to set it in a plugin, but encountered an error. This could be due to the timing of when the plugin runs. Instead of using the useI18n composable directly in the plugin, you might want to try using the i18n:ready hook. Here's an example of how you could do this:
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('i18n:ready', ({ i18n }) => {
const locale = i18n.global.locale.value
dates.locale(locale)
})
})

export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('i18n:ready', ({ i18n }) => {
const locale = i18n.global.locale.value
dates.locale(locale)
})
})

This approach is suggested in the Stack Overflow post about localization in Nuxt3. 2. Use a composable for locale management: You could create a composable to manage the locale and ensure it's set early in the application lifecycle. This approach is demonstrated in the Nuxt documentation on state management. Here's a simplified example:
export const useLocale = () => {
const locale = useState<string>('locale', () => 'en') // default to 'en'

// Set the locale as soon as possible
if (process.client) {
locale.value = /* logic to determine client locale */
}

watch(locale, (newLocale) => {
dates.locale(newLocale)
}, { immediate: true })

return locale
}

export const useLocale = () => {
const locale = useState<string>('locale', () => 'en') // default to 'en'

// Set the locale as soon as possible
if (process.client) {
locale.value = /* logic to determine client locale */
}

watch(locale, (newLocale) => {
dates.locale(newLocale)
}, { immediate: true })

return locale
}

You can then use this composable in your components or plugins. 3. Consider using the new experimental.headNext option: Nuxt 3 has introduced an experimental feature that might help with hydration issues. As mentioned in the Nuxt Configuration documentation, you can enable experimental.headNext: true in your nuxt.config.ts. This uses new head optimizations and a hash hydration plugin which might help resolve hydration mismatches. 4. Ensure consistent server and client rendering: Make sure that your date formatting logic is consistent between server-side rendering and client-side hydration. You might want to check out the YouTube video on Server Rendering and Global State in Nuxt for more insights on this topic. If none of these solutions work, you might want to consider filing an issue on the nuxt-i18n GitHub repository, as there could be a bug in how the composable is handled in plugins. Remember, hydration errors can be tricky to debug. The new Vue 3.4 features, as discussed in this YouTube video, provide improved hydration error messages which might help you pinpoint the exact cause of the mismatch. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@277205807937093633> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Reinier Kaper
Reinier KaperOP3w ago
When I access a French route, i18n always incorrectly returns en as the locale in a plugin:
export default defineNuxtPlugin((nuxtApp) => {
console.log(nuxtApp.$i18n.locale.value) // always `en`
})
export default defineNuxtPlugin((nuxtApp) => {
console.log(nuxtApp.$i18n.locale.value) // always `en`
})
@kapa.ai
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: Sorry @Reinier Kaper, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
Mittchel
Mittchel3w ago
who does that
Reinier Kaper
Reinier KaperOP3w ago
Who does what? @Bobbie sorry for the ping. I can't repro this in Stackblitz, everything looks normal there. Our config is pretty much the same, but for some reason the server always returns en, instead of the right locale. Any pointers as to where we might need to look? This is our config:
{
vueI18n: './i18n.config.ts',
restructureDir: 'src',
locales: [
{
code: 'en',
name: 'English',
language: 'en',
file: 'en.js',
},
{
code: 'fr',
name: 'Français',
language: 'fr',
file: 'fr.js',
},
],
lazy: true,
langDir: 'locales',
defaultLocale: 'en',
baseUrl: process.env.NUXT_PUBLIC_BASE_URL,
detectBrowserLanguage: false,
strategy: 'prefix_and_default',
compilation: {
strictMessage: false,
},
},
{
vueI18n: './i18n.config.ts',
restructureDir: 'src',
locales: [
{
code: 'en',
name: 'English',
language: 'en',
file: 'en.js',
},
{
code: 'fr',
name: 'Français',
language: 'fr',
file: 'fr.js',
},
],
lazy: true,
langDir: 'locales',
defaultLocale: 'en',
baseUrl: process.env.NUXT_PUBLIC_BASE_URL,
detectBrowserLanguage: false,
strategy: 'prefix_and_default',
compilation: {
strictMessage: false,
},
},
And root config file
export default defineI18nConfig(() => ({
legacy: false,
availableLocales: ['en', 'fr'],
fallbackLocale: 'en',
fallbackWarn: import.meta.dev,
globalInjection: true,
}))
export default defineI18nConfig(() => ({
legacy: false,
availableLocales: ['en', 'fr'],
fallbackLocale: 'en',
fallbackWarn: import.meta.dev,
globalInjection: true,
}))
Bobbie
Bobbie3w ago
If you can reproduce the issue in a fresh project locally you can share it as a public repository if it's not possible to reproduce inside stackblitz. Recent patch releases may have already resolved this issue, ensure you have the latest version installed, other than that I can't really debug this without a minimal reproduction project 🙏
Reinier Kaper
Reinier KaperOP3w ago
Yeah I'm trying to get the starter to fail, but I can't. It's something in our project, but even if I disable all other modules it seems to go wrong. Very hard to pin-point and we can't update Nuxt because it brakes other modules. I'll see if I can update to latest i18n though and see if that helps
Bobbie
Bobbie3w ago
What version are you currently on?
Reinier Kaper
Reinier KaperOP3w ago
nuxt: 3.15.2 i18n: 9.1.3
Bobbie
Bobbie3w ago
Both 9.1.4 and 9.1.5 target regressions/issues with server-side locale setting and detection, so I wouldn't be surprised if updating resolves your issue.
Reinier Kaper
Reinier KaperOP3w ago
Okay, let me update to latest, just to rule it out. Even if we can't upgrade yet. Thanks for the help! Oeh, yes I think that fixed everything. Pfew! I'll manually update the package and deploy it, thanks! Interesting: after releasing our update in the wild, we now seem to get to opposite issue: where the server renders French, instead of English (the requested language). I'll keep an eye on it, but this feels like the fix wasn't fully effective I can't reproduce this myself though, so I wonder if there's an aspect of caching going on
Bobbie
Bobbie3w ago
I'm not sure if that could be due to changes in the module, but do keep an eye on it, if you can reproduce it I'll give it a look 🙏
Reinier Kaper
Reinier KaperOP3w ago
👍 Okay this still keeps happening, I have a feeling it's based on the user's OS. Previously this was an issue too, where the following oddity occurred: - English OS - English i18n selection: good - English OS - French i18n selection: good - French OS - English i18n selection: good - French OS - French i18n selection: server render in English I wonder if this is still an issue, as I see people landing on /fr/some-route and the server rendering in English again. I will investigate more today, hopefully I can reproduce it again like last time. At least I now know where to look. Additional question: if people visited the site before (say a week ago) and they revisit again today (fixed version of the site), would there still be something cached (cookie/localstorage/broswer-cache/etc?) that could cause the "old" behaviour temporarily?
Reinier Kaper
Reinier KaperOP3w ago
Examples of "inverse" things happening on the latest version:
No description
No description
Reinier Kaper
Reinier KaperOP3w ago
location-vr is a French route, rv-rental is the English counterpart (English is our default language btw)

Did you find this page helpful?