Open steffenstolze opened 4 months ago
This is very problematic, because accessing the index page of any website that uses Nuxt i18n leads to a 404 error.
Based on your workaround, I've created a little module to automatically redirect the user to its preferred language. Create a modules/i18n-fix-index.ts
file with the following content.
import * as fs from 'fs'
import { createResolver, defineNuxtModule, useLogger } from '@nuxt/kit'
import type { PrerenderRoute, Nitro } from 'nitropack'
/**
* Options for the module.
*
* @interface
*/
export interface ModuleOptions {
/**
* Accepted languages codes.
*/
acceptedLanguages: string[]
/**
* The cookie name.
*/
i18nCookieName: string
}
/**
* The name of the module.
*/
const name = 'i18n-fix-index'
/**
* The logger instance.
*/
const logger = useLogger(name)
/**
* TODO: Should be removed once "https://github.com/nuxt-modules/i18n/issues/3016" is fixed.
*/
export default defineNuxtModule<ModuleOptions>({
meta: {
name,
version: '0.0.1',
compatibility: { nuxt: '^3.0.0' },
configKey: 'i18nFixIndex'
},
defaults: {
acceptedLanguages: ['en', 'fr'],
i18nCookieName: 'i18n_redirected'
},
setup: async (options, nuxt) => {
const defaultLanguage = options.acceptedLanguages[0]
const indexPageContent = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="refresh" content="3; url=/${defaultLanguage}/">
<script type="text/javascript">
const getCookieValue = (name) => (
document.cookie.match('(^|;)\\\\s*' + name + '\\\\s*=\\\\s*([^;]+)')?.pop() || ''
)
const acceptedLanguages = ${JSON.stringify(options.acceptedLanguages)}
const i18nCookieValue = getCookieValue('${options.i18nCookieName}')
let wantedLanguage = acceptedLanguages[0]
if (acceptedLanguages.includes(i18nCookieValue)) {
wantedLanguage = i18nCookieValue;
} else {
const userLanguage = navigator.language || navigator.userLanguage
const languageCode = userLanguage.indexOf('-') === -1 ? userLanguage : userLanguage.split('-')[0]
if (acceptedLanguages.includes(languageCode)) {
wantedLanguage = languageCode
}
}
document.querySelector('meta[http-equiv="refresh"]').setAttribute('content', '0; /' + wantedLanguage + '/')
document.querySelector('p > a').setAttribute('href', '/' + wantedLanguage + '/')
</script>
</head>
<body>
<p style="margin-bottom: 0;">
Redirecting you... Please wait or click <a href="/${defaultLanguage}/">here</a> if you're not being redirected.
</p>
</body>
</html>`
const resolver = createResolver(import.meta.url)
const hooks = nuxt.options.nitro.hooks ?? {}
// @ts-expect-error: We're not in nuxt.config.ts.
hooks['prerender:generate'] = (route: PrerenderRoute, nitro: Nitro) => {
if (route?.route === '/200.html') {
logger.info('Fixing index...')
const outputPath = resolver.resolve(
nitro.options.output.publicDir,
'index.html'
)
fs.writeFileSync(outputPath, indexPageContent)
logger.success('Done. Don\'t forget to delete this module once https://github.com/nuxt-modules/i18n/issues/3016 is closed.')
}
}
nuxt.options.nitro.hooks = hooks
}
})
Then, in your nuxt.config.ts
, you have to configure it.
i18nFixIndex: {
acceptedLanguages: ['en', 'fr'], // Put the accepted languages codes. The first of the list will be the default language.
i18nCookieName: 'i18n_redirected' // `cookieKey` (https://i18n.nuxtjs.org/docs/options/browser#cookiekey) of Nuxt i18n.
}
Confirmed, I have tried to change the versions of each dependency and the exact same thing happens to me.
combined with #3062 means prefix strategy is pretty broken atm.
Ran into this today. After puzzling for quiet a while, I (think) I fixed it with the following changes:
prefix_and_default
: // nuxt.config.ts
i18n: {
strategy: 'prefix_and_default',
This will cause the index.html
to be generated in the root.
But now we miss the defaultLocale in the url ... so to fix this, I added a simple middleware:
In the /middleware
folder (create it, if it doesn't exist), create a file ... call it: redirect-root.global.ts
(or .js
). The name doesn't really matter, except the .global.
-part ... that's important!
The content of this middleware:
export default defineNuxtRouteMiddleware((to, from) => {
const { $i18n } = useNuxtApp();
if (to.path === '/') {
const defaultLocale = $i18n.defaultLocale || $i18n.fallbackLocale || 'en';
return navigateTo(`/${defaultLocale}`);
}
});
Now we will be redirected from the / to the defaultLocale 👍
Ran into this today. After puzzling for quiet a while, I (think) I fixed it with the following changes:
- change the strategy back to
prefix_and_default
:// nuxt.config.ts i18n: { strategy: 'prefix_and_default',
This will cause the
index.html
to be generated in the root.But now we miss the defaultLocale in the url ... so to fix this, I added a simple middleware:
In the
/middleware
folder (create it, if it doesn't exist), create a file ... call it:redirect-root.global.ts
(or.js
). The name doesn't really matter, except the.global.
-part ... that's important!The content of this middleware:
export default defineNuxtRouteMiddleware((to, from) => { const { $i18n } = useNuxtApp(); if (to.path === '/') { const defaultLocale = $i18n.defaultLocale || $i18n.fallbackLocale || 'en'; return navigateTo(`/${defaultLocale}`); } });
Now we will be redirected from the / to the defaultLocale 👍
thank u! I'll update to latest version and try your fix. Looks good and simple :)
Sadly this solution from @skarnl doesn't work (with SSG) to me because by not landing in index.html
the middleware doesn't do its job. In the end I created a script that adds in index.html with the old school trick to redirect: <meta http-equiv="refresh" content="0; url=/en" />
. Another solution could be to copy the index.html of the main language also in the root (npm run generate && cp .output/public/en/index.html .output/public/index.html )
I have also detected that the problem is more serious than it seems, there are pages that I generate from CMS that are generated without problems, but the local pages are not, none at all. There is no index for these pages.
+1 I'm eager to have update on this matter as it is problematic to not be able to statically render any nuxt site using @nuxt/i18n
Downgrading versions to
"dependencies": { "@nuxtjs/i18n": "8.3.0", "nuxt": "3.11.2" }
does not do the trick on vercel with nuxt build --prerenderr
or nuxt generate
with static: true
(cf: https://nuxt.com/blog/going-full-static#current-issues)
If anyone got it working by downgrading versions I would like to know wich ones you used :)
I still use the same "hack" from my original post where I create a nitro hook:
nitro: {
static: true,
// Hacky approach to redirect create index.html file for root redirect
hooks: {
'prerender:generate'(route, nitro) {
if (route?.route === '/200.html') {
const redirectHtml =
'<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=/de"></head></html>';
const outputPath = path.join(
nitro.options.output.publicDir,
'index.html'
);
writeFileSync(outputPath, redirectHtml);
}
},
},
},
it still works flawlessly.
writeFileSync
How do you import writeFileSync
in your config ?
+1 I'm eager to have update on this matter as it is problematic to not be able to statically render any nuxt site using @nuxt/i18n
Downgrading versions to
"dependencies": { "@nuxtjs/i18n": "8.3.0", "nuxt": "3.11.2" }
does not do the trick on vercel withnuxt build --prerenderr
ornuxt generate
withstatic: true
(cf: https://nuxt.com/blog/going-full-static#current-issues)If anyone got it working by downgrading versions I would like to know wich ones you used :)
The answer is in the first post.
Ok, so I got it working but slightly differently from the version in the first post :
import fs from 'node:fs'
import path from 'node:path'
export default defineNuxtConfig({
target: 'static',
nitro: {
hooks: {
'prerender:generate'(route, nitro) {
if (route?.route === '/200.html') {
const redirectHtml =
'<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=/fr"></head></html>';
const outputPath = path.join(
nitro.options.output.publicDir,
'index.html'
);
fs.writeFileSync(outputPath, redirectHtml);
}
},
},
},
})
So the 3 differences are :
Hope it can help someone :)
Environment
Darwin
v20.15.0
^3.12.3
3.12.0
-
npm
-
devtools
,ssr
,nitro
,experimental
,modules
,i18n
@nuxtjs/i18n@^8.3.1
-
Reproduction
https://github.com/steffenstolze/nuxt_i18n_bug
Describe the bug
Running
nuxt generate
should create anindex.html
file in the.output/public
folder when usingstrategy: 'prefix'
in the i18n config. If this file is missing, you get a 404 when accessing root, since every route is prefixed with the locale code.With the latest Nuxt and Nuxt i18n versions, it doesn't:
With older ones it works up to these versions:
From here on, if you bump up
@nuxtjs/i18n
to8.3.1
ornuxt
to3.12.0
and higher, the index.html is not created anymore.Additional context
As a workaround I added the following to the
nitro
config, to generate the index.html manually once the 200.html file is created.While this works, it also feels super hacky and shouldn't be needed.
Logs
No response