nextjs dynamic routes doesn't work with next-i18next - next.js

I just added next-i18next in my nextjs project following the official guide and everything seemed to be in order, but when I change the language from default (Italian) to English and I go to the detail of an entity then I get 404. This happens only with the dynamic routes and only with the language that is not the default one.
I am going to show you more details.
My next-i18next.config.js file:
module.exports = {
i18n: {
defaultLocale: "it",
locales: ["it", "en"],
},
};
[id].tsx
//My NextPage component...
export async function getStaticPaths() {
const paths = await projects.find()?.map((_project) => ({
params: { id: _project.id + "" },
}));
return {
paths,
fallback: false,
};
}
export async function getStaticProps({
locale,
...rest
}: {
locale: string;
params: { id: string };
}) {
const project = await projects.findOne({id: rest.params.id})
const seo: Seo = {
//...
};
//This row is not the problem, since it persists even if I remove it. Also I am sure that the project exists.
if (!project?.id) return { notFound: true };
return {
props: {
...(await serverSideTranslations(locale, [
"common",
"footer",
"projects",
])),
seo,
project,
},
};
}
index.tsx (under projects folder)
const Projects: NextPage<Props> = ({ /*...*/ }) => {
//...
const router = useRouter();
return <button onClick={() =>
router.push({
pathname: `/projects/[slug]`,
query: { slug: project.slug },
})
}>Read more</button>
}
Also I get the error Error: The provided 'href' (/projects/[slug]) value is missing query values (slug) to be interpolated properly. when I try to change the language while I am in the detail of the project with the italian language set, but I think I did it right according to this doc. As I said before, instead, if I try to go into the dynamic route after having changed the language to "en" then I go to 404 page.
Do you have any suggestions to solve this problem?

I solved this by updating the mothod getStaticPaths to:
export async function getStaticPaths({ locales }: { locales: string[] }) {
const projects = getProjects({ locale: "it" });
const paths = projects.flatMap((_project) => {
return locales.map((locale) => {
return {
params: {
type: _project.slug,
slug: _project.slug,
},
locale: locale,
};
});
});
return {
paths,
fallback: true,
};
}
So there must be passed the locale into the paths.

Related

getStatic Path not working for base URL "/" in NextJS

I'm using Prismic and NextJS for the first time.
What I'm trying to is make it so when the user goes to the base url for the page localhost:3000/ in dev something will load. /About and /Pricing are working fine the base url doesn't work.
import { GetStaticPaths, GetStaticProps } from 'next'
import { SliceZone } from '#prismicio/react'
import * as prismicH from "#prismicio/helpers";
import { createClient, linkResolver } from '../../prismicio'
import { components } from '../../slices'
interface HomePageProps {
page: any
}
const HomePage = (props:HomePageProps) => {
return <SliceZone slices={props.page.data.slices} components={components} />
}
export default HomePage
interface HomePageStaticProps {
params: any
previewData:any
}
export async function getStaticProps(props:HomePageStaticProps) {
console.log("DOES NOT FIRE FOR localhost:3000")
const client = createClient({ previewData:props.previewData })
const params = props.params;
const uid = params?.pagePath?.[params.pagePath.length - 1] || "home";
const page = await client.getByUID("page", uid);
return {
props: {
page,
},
}
}
export const getStaticPaths: GetStaticPaths = async () => {
const client = createClient();
const pages = await client.getAllByType("page");
const paths = pages.map((page) => prismicH.asLink(page, linkResolver)) as string[];
console.log(paths) // [ '/pricing', '/about', '/' ]
return {
paths,
fallback: false,
};
}
or to simplify it further
[[...pagePath]].tsx fails when going to localhost:3000/ but does not fail on localhost:3000/about or localhost:3000/pricing.
import { GetStaticPaths, GetStaticProps } from 'next'
interface HomePageProps {
page: string
}
const HomePage = (props:HomePageProps) => {
return <>{props.page}</>
}
export default HomePage
interface HomePageStaticProps {
params: any
previewData:any
}
export async function getStaticProps(props:HomePageStaticProps) {
const params = props.params;
const uid = params?.pagePath?.[params.pagePath.length - 1] || "home";
//const page = await client.getByUID("page", uid);
return {
props: {
page:uid,
},
}
}
export const getStaticPaths: GetStaticPaths = async () => {
const paths = [ '/pricing', '/about', '/' ];
return {
paths,
fallback: false,
};
}
As far as I can see your'e not fetching the right way. In order to to have a clean project I would recommend to use a const var where you can determine between dev and production enviorenment. To do so you can simply create a file for example: constants.js containing the following:
export const baseurl = process.env.NODE_ENV === "production"
? process.env.NEXT_PUBLIC_DOMAIN // <-- your domain on live
: "http://localhost:3000"; // localhost on dev
Now with this you automatically have localhost on your dev. Notice that you need http:// which your'e missing at the moment. Now the next example shows you how to fetch something on your root / by entering the following code:
import { baseurl } from "../utils/constants"; // <-- importing the constant
// This function gets called at build time on server-side.
// It won't be called on client-side, so you can even do
// direct database queries.
export async function getStaticProps() {
// Call an external API endpoint to get posts.
// You can use any data fetching library
const res = await fetch(`${baseurl}/api/posts`)
const posts = await res.json()
// By returning { props: { posts } }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
}
}
If you are using Create-T3-App
https://github.com/t3-oss/create-t3-app
then
your next.config.mjs will default to this as of Nov 7, 2022
const config = {
reactStrictMode: true,
swcMinify: true,
i18n: {
locales: ["en"],
defaultLocale: "en",
},
};
export default config;
remove
const config = {
reactStrictMode: true,
swcMinify: true,
//i18n: {
// locales: ["en"],
// defaultLocale: "en",
//},
};
export default config;
This will make default "/" pathing work, if you require i18n, I'm sorry I can't help.

NextJS: getServerSideProps - Is there a problem to use props object with redirect in the same return?

I'm using Next.js with Typescript, and I'm having some troubles to correctly type my props that getServerSideProps would return me. On getServerSideProps, or as I call it, getServerSidePropsImpl, I check user authentication and decide if I give it a redirect or the data for it to initialize. The problem with this is that Typescript doesn't correctly type my props, or give me some errors.
So, i had the idea to :
// /pages/sheet/1.tsx
import React from 'react';
import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
import database from '../../utils/database';
import { sessionSSR } from '../../utils/session';
export default function Sheet1(props: InferGetServerSidePropsType<typeof getServerSidePropsImpl>): JSX.Element {
//...Do some stuff...
return <></>;
}
async function getServerSidePropsImpl(ctx: GetServerSidePropsContext) {
const player = ctx.req.session.player;
if (!player) {
return {
redirect: {
destination: '/',
permanent: false
},
props: {
playerID: 0,
playerInfo: []
}
};
}
const playerID = player.id;
const results = await Promise.all([
database.playerInfo.findMany({
where: {
player_id: playerID
},
select: {
info: true,
value: true
},
})
]);
return {
props: {
playerID,
playerInfo: results[0]
}
};
}
export const getServerSideProps = sessionSSR(getServerSidePropsImpl);
To make sure Typescript would correctly type my props object, I had to put generic values when returning a redirect:
return {
redirect: {
destination: '/',
permanent: false
},
props: {
playerID: 0,
playerInfo: []
}
};
Is there any issues to this approach, giving the amount of props will increase as I make this component?

I get a 404 page in vercel after adding next-i18next

I have added the app next-i18next for internationalisation. After that, all pages in vercel become 404 pages.
By the way, it works perfectly in locally.
WHY??
const { i18n } = require("./next-i18next.config");
module.exports = {
i18n: {
locales: ["en", "fr", "nl-NL", "nl-BE", "de", "ja"],
defaultLocale: "ja",
https://skripts.vercel.app/
Above is the URL of my site. There is a this error in the log.
Failed to load resource: the server responded with a status of 500 ()
Maybe you need to add "getStaticProps" to Dynamic Routing Pages?
// pages/blog/[slug].js
export const getStaticPaths = ({ locales }) => {
return {
paths: [
// if no `locale` is provided only the defaultLocale will be generated
{ params: { slug: 'post-1' }, locale: 'en-US' },
{ params: { slug: 'post-1' }, locale: 'fr' },
],
fallback: true,
}
}
This described here:
https://nextjs.org/docs/advanced-features/i18n-routing#dynamic-routes-and-getstaticprops-pages
I have React + Next.js + next-i18next + Vercel application. And when I deployed to the Vercel I start getting an errors in console:
Internal Server Error 500 with all *.json files.
I just added "locale" keys->values and this is solve my problem.
SOLVED
Please read https://github.com/i18next/next-i18next#vercel-and-netlify this will solve all the problemas
const path = require('path');
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'es'...],
},
...(typeof window === undefined
? { localePath: path.resolve('./public/locales') }
: {}),
};
code by #stophecom
It happened to me and the only solution that I found is to generate all the pages (4000), almost 20 minutes to do a deploy :(
const getStaticPaths = async () => {
const res = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/xxx`);
const projects = await res.json();
const slugs = projects.map((p) => slugifyForFront(p.slug));
const { locales } = i18n;
const paths = slugs.flatMap((slug) =>
locales.map((locale) => ({
params: { slug },
locale: locale,
}))
);
return { paths, fallback: false };
};
Probably is not the best solution but I could not make it work using fallback: true | blocking always 404
I hope some one post a better solution

How to use changeLanguage method in next-i18next for changing locale?

I'm trying to change default locale in my project with just button click. I don't want to change my URL with pushing sub paths like fooo.com/fa.
Here is my next-i18next config:
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fa'],
},
};
And here is my simple code for changing locale and using that:
const { t, i18n } = useTranslation('common');
///
<button onClick={() => i18n.changeLanguage('fa')}>
Click to Change Language
</button>
<p>{t('title')}</p>
But it does not work and stuck in default locale that is EN.
This worked for me:
const changeLocale = (locale) => {
router.push({
route: router.pathname,
query: router.query
}, router.asPath, { locale });
}
I had something similar and came up with this:
pages/some-route
export async function getAllTranslationsServerSide(locale: string) {
return serverSideTranslations(
locale,
["common"],
nextI18nextConfig,
nextI18nextConfig.i18n.locales
);
}
export async function getStaticProps({ locale }) {
return {
props: {
...(await getAllTranslationsServerSide(locale)),
},
};
}
components/SomeComponent.tsx
function changeLanguage(locale) {
i18n.changeLanguage(locale);
router.push({ pathname, query }, asPath, {
locale,
scroll: false,
shallow: true,
});
}
I wanted to change locale (including the sub-path as my project uses NextJS sub-path routing) but without re-triggering other API requests that a route might need and hence any potential re-renders (I was getting some ugly white flashes).
The key bit is loading all locales in getStaticProps.
This means that when changeLanguage is called the required translation strings are already loaded client-side.
This works for my small app but is probably a bad idea for apps with more translations.

NextJS routing error, when changing pages, the wrong file is trying to open

What I want
I want to change pages without next thinking I am trying to open another page.
The Problem
I have this weird routing problem.
First, my folder structure
pages
[app]
[object]
index.js
index.js
manager.js
feed.js
I am at this path /[app] and navigate to /[app]/manager and then I want to navigate to /[app]/feed and I get this Unhandled Runtime Error.
TypeError: Cannot read property "title" of undefined
This error comes from [object] index.js. Stacktrace is below. Of course, it makes sense it cannot read title because I am trying to open another page. And yet it thinks I am trying to open [object].
This error happens from time to time, but it doesn't matter in what order I try to open the pages, it can be manager to feed or feed to manager, or whatever else I have there.
My getStaticPaths and getStaticProps are the same on all these pages, I will share the one for manager.js.
export const getStaticPaths = async () => {
const paths = appRoutes.map((appRoute) => {
const slug = appRoute.slug;
return {
params: {
app: slug,
manager: 'manager',
},
};
});
return {
fallback: false,
paths,
};
};
export const getStaticProps = async ({ locale }) => {
return {
props: {
...(await serverSideTranslations(locale, ['manager', 'common'])),
},
};
};
And the same again, but for [object]:
export const getStaticPaths = async () => {
const allObjects = await loadObjectData({ id: 'all' });
const paths = allObjects.flatMap((object) => {
return appRoutes.map((appRoute) => {
return {
params: {
object: object.type,
app: appRoute.slug,
},
};
});
});
return {
fallback: false,
paths,
};
};
export const getStaticProps = async ({ params, locale }) => {
const object = await loadObjectData({ type: params.object });
const app = appRoutes.find((appRoute) => appRoute?.slug === params.app);
if (!object) {
throw new Error(
`${object} is not a valid Object. Try checking out your parameters: ${params.object}`
);
}
if (!app) {
throw new Error(`${app} is not a valid App.`);
}
return {
props: {
...(await serverSideTranslation(locale, ['common'])),
object,
app,
},
};
};
This error is hard to reproduce because it happens only from time to time.
New Edits
This is the full file of [object]/index.js
import appRoutes from '../../../routes/appRoutes';
import loadObjectData from '../../../utils/loadObjects';
import { serverSideTranslation } from 'next-i18next/serverSideTranslations';
export default function ObjectPage({ object }) {
return <h1> {object.title} </h1>;
}
export const getStaticPaths = async () => {
const allObjects = await loadObjectData({ id: 'all' });
const paths = allObjects.flatMap((object) => {
return appRoutes.map((appRoute) => {
return {
params: {
object: object.type,
app: appRoute.slug,
},
};
});
});
return {
fallback: false,
paths,
};
};
export const getStaticProps = async ({ params, locale }) => {
const object = await loadObjectData({ type: params.object });
const app = appRoutes.find((appRoute) => appRoute?.slug === params.app);
if (!object) {
throw new Error(
`${object} is not a valid Object. Try checking out your parameters: ${params.object}`
);
}
if (!app) {
throw new Error(`${app} is not a valid App.`);
}
return {
props: {
...(await serverSideTranslation(locale, ['common'])),
object,
app,
},
};
};
Stacktrace:
ObjectPage: index.js:6 Uncaught TypeError: Cannot read property 'title' of undefined
at ObjectPage (http://localhost:3000/_next/static/chunks/pages/%5Bapp%5D/%5Bobject%5D.js:3733:21)
at div
at Grid (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:13654:35)
at WithStyles (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:179881:31)
at div
at StyledComponent (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:179652:28)
at div
at ProjectSelectionStore (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:234820:77)
at Layout (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:278:23)
at TaskStore (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:235454:77)
at UserDocumentStore (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:235663:77)
at StoneStore (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:235119:77)
at StoreMall (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:409:23)
at ThemeProvider (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:178584:24)
at App (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:234333:24)
at I18nextProvider (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:224427:19)
at AppWithTranslation
at ErrorBoundary (http://localhost:3000/_next/static/chunks/main.js?ts=1624290251377:146:47)
at ReactDevOverlay (http://localhost:3000/_next/static/chunks/main.js?ts=1624290251377:250:23)
at Container (http://localhost:3000/_next/static/chunks/main.js?ts=1624290251377:8662:5)
at AppContainer (http://localhost:3000/_next/static/chunks/main.js?ts=1624290251377:9151:24)
at Root (http://localhost:3000/_next/static/chunks/main.js?ts=1624290251377:9282:24)
25.06.2021
So I consoled logged the router from the ObjectPage and for each NavigationItem. I noticed something strange.
This is the href I am passing to teh <Link>:
{
pathname: "/[app]/[menuItem]"
query: {
app: "content"
menuItem: "files"
}
}
And this is the full router I am getting back on ObjectPage.
{
asPath: "/content/editor" // this the path i want to open
back: ƒ ()
basePath: ""
beforePopState: ƒ ()
components: {
"/[app]/[object]": {styleSheets: Array(0), __N_SSG: true, __N_SSP: undefined, props: {…}, Component: ƒ}
"/[app]/editor": {initial: true, props: {…}, err: undefined, __N_SSG: true, Component: ƒ, …}
"/_app": {styleSheets: Array(0), Component: ƒ}
}
defaultLocale: "de"
events: {on: ƒ, off: ƒ, emit: ƒ}
isFallback: false
isLocaleDomain: false
isPreview: false
isReady: true
locale: "de"
locales: ["de"]
pathname: "/[app]/[object]" // [object] is being loaded
prefetch: ƒ ()
push: ƒ ()
query: {app: "content", menuItem: "editor", object: "editor"} // this is interesting
reload: ƒ ()
replace: ƒ ()
route: "/[app]/[object]" // same as pathname
}
In the query you can see object was injected. But I cannot tell from where and why.
I had this code:
{
pathname: "/[app]/[menuItem]"
query: {
app: "content"
menuItem: "files"
}
}
This was incorrect because there is no dynamic path to [menuItem]. So instead I wrote:
{
pathname: "/[app]/files"
query: {
app: "content"
}
}
Which fixed the issue I had.
I have misunderstood the docs for parameters.

Resources