I try to create multilanguage website with Next JS.
I'm using next-translate package for translation.
For the posts I use static generation.
|-> pages
|-> post
|-> [slug].js
|-> i18n.json
|-> next.config.js
Problem
When I use default language ('tr') I successfully navigate http://localhost:3000/post/title-tr
But If I changed to language to 'en', library add 'en' to path then try to navigate http://localhost:3000/en/post/title-en and returns 404 page.
Solution I tried
In next.config.js I add rewrites method. But It didn't work.
require('dotenv').config();
const nextTranslate = require('next-translate');
module.exports = nextTranslate({
async rewrites() {
return [
{
source: '/en/post/:path*',
destination: '/post',
},
]
},
env: {
NEXT_PUBLIC_URL: process.env.NEXT_PUBLIC_URL,
},
})
Where should I solve this problem? Routing, config files or Link component?
i18n.json
{
"locales": ["tr", "en"],
"defaultLocale": "tr",
"pages": {
"*": ["common","country","position"]
}
}
Link component I used
<Link key={`L${item.id}`} href="/post/[slug]" as={`/post/${slug(item.title)}-${item.id}`}></Link>
[slug].js
function Post({ data }){
return <>...</>
}
export async function getStaticPaths() {
const { data } = await fetch('fromAPI');
return {
paths: data.map(item => {
return {
params:
{
slug: `${slug(item.title)}-${item.id}`
}
}
}),
fallback: false
}
}
export async function getStaticProps({ params }) {
const { data } = await fetch('fromAPI');
return { props: { data} }
}
export default Post;
You have to add a path for every locale as shown in the documentation: https://nextjs.org/docs/advanced-features/i18n-routing#dynamic-routes-and-getstaticprops-pages
// [slug].js
export const getStaticPaths = ({ locales }) => {
return {
paths: [
{ params: { slug: 'post-1' }, locale: 'tr' },
{ params: { slug: 'post-1' }, locale: 'en' },
],
fallback: true,
}
}
When I use default language ('tr') I successfully navigate http://localhost:3000/post/title-tr
But If I changed to language to 'en', library add 'en' to path then
try to navigate http://localhost:3000/en/post/title-en and returns 404
page.
If no locale is provided only the defaultLocale will be generated.
If fallback is false, then any paths not returned by getStaticPaths will result in a 404 page. So right now you're only generating the defaultLocale "tr" and all other paths with different locales will result in a 404 page.
Related
It's been some days I'm trying to fix this problem but without success... I'm trying tu use next-i18next to get some translations keys coming from a CMS. We are in SSR, that why I use getServersSideProps. But got the ._nextI18Next.userConfig.use[0]returned fromgetServerSideProps` in "/" error. Do you guys got some ideas ? Am I on a bad way ? Thanks a lot for your time and your help.
Here is my next-i18next.config.js :
/* eslint-disable #typescript-eslint/no-var-requires */
const i18nextHttpBackend = require('i18next-http-backend')
const i18nextBrowserLanguageDetector = require('i18next-browser-languagedetector')
const axios = require('axios')
module.exports = {
i18n: {
defaultLocale: 'default',
locales: ['default', 'gb', 'fr']
},
use: [i18nextHttpBackend, i18nextBrowserLanguageDetector],
debug: true,
fallbackLng: 'fr',
interpolation: {
format: (value, format, lng) => {
if (value instanceof Date) {
return DateTime.fromJSDate(value).setLocale(lng).toLocaleString(DateTime[format])
}
return value
}
},
backend: {
loadPath: lng => {
axios.get(`https://my_api_url/${lng}/formatted-transkeys`).then(res => {
return JSON.stringify(res.data)
})
}
},
serializeConfig: false
}
My next.config.js :
/* eslint-disable #typescript-eslint/no-var-requires */
const { i18n } = require('./next-i18next.config')
const nextConfig = {
distDir: 'dist',
reactStrictMode: false,
i18n,
trailingSlash: true,
images: {
domains: ['d1m7xnn75ypr6t.cloudfront.net']
}
}
module.exports = nextConfig
And my index.tsx :
interface i18nProps {
locale: string
}
export const getServerSideProps = async (props: i18nProps) => {
const result = await getHomePageData(props.locale)
const data = result.data
return {
props: {
data: data,
...(await serverSideTranslations(props.locale, ['search']))
}
}
}
export default Homepage
Trying to be as clear as I can, ask me everything you need.
Thanks,
R.
try
const i18nextHttpBackend = require('i18next-http-backend/cjs')
instead of
const i18nextHttpBackend = require('i18next-http-backend')
and you need to pass the config here:
import nextI18NextConfig from '../next-i18next.config.js';
// ...
...(await serverSideTranslations(props.locale, ['search'], nextI18NextConfig))
Like described here: https://github.com/i18next/next-i18next#unserialisable-configs
compare it also with this example: https://github.com/i18next/i18next-http-backend/tree/master/example/next
there!
I want to configure the first screen as a login page.
However, after logging in, we want to prevent the user from going to the login page after confirming the login with the cookie value.
The configuration file is as below, and how can I solve it?
next.config.js
module.exports = {
async redirects() {
return [
{
source: "/",
destination: "/login",
permanent: false,
has: [
{
type: "cookie",
key: "authorized",
value: "access_token",
},
],
},
];
},
};
This doesn't look possible to me, since in the config we only can have static values, and authtoken will change for every login, UI side redirection must be handled from separate AuthContext like we do with react apps.
Another alternative to above approach
is having one more cookie like 'authorized' and it will have value let say true of false. So we can check for 'authorized' is it has value 'true', next.config is below for the same.
Reference: https://nextjs.org/docs/api-reference/next.config.js/redirects#header-cookie-and-query-matching
{
reactStrictMode: true,
async redirects() {
return [
{
source: '/',
destination: '/login',
permanent: true,
has: [
{
type: 'cookie',
key: 'authorized',
value: 'false',
},
],
},
]
},
}
A more robust and controllable approach would be using something like nextAuth
You would have to follow two steps
In order to cover both server side and client-side scenarios (users logging directly on a logged in page, implement both these ) you can conditionally redirect using a router.push on client and getInitialProps with a 302 on the server side
i.e Using nextAuth
import { useSession, getSession } from "next-auth/react"
export default function Page() {
const { data: session, status } = useSession()
if (status === "loading") {
return <p>Loading...</p>
}
if (status === "unauthenticated") {
return <p>Access Denied</p>
}
// Do a router.push here
}
Serverside
import { useSession, getSession } from "next-auth/react"
export default function Page() {
const { data: session } = useSession()
if (typeof window === "undefined") return null
if (session) {
// do a 302 redirect, using ctx.resShead via getInitialprops
return <p>Access Denied</p>
}
export async function getServerSideProps(context) {
return {
props: {
session: await getSession(context),
},
}
}
In order for nextAuth to get cookies, declare it as a provider
see example here - https://stackoverflow.com/a/69418553/13749957
I am building a Next.js app with internationalization using next-i18next. Pages are generated for all the pages of my site for both English and French, except for pages with dynamic routes: (i.e., blog/[id]/[blog-title]). For pages with dynamic routes, pages are generated for English, but not for French.
I should note that the blog entries are the same in both languages. So if the user click on a blog entry in the list, they will get the same blog entry.
When a French language user goes to a page with a dynamic route they get a 404. I am new to React and Next so I could be doing something dumb here.
// next-i18next.config.js
module.exports = {
i18n: {
locales: ['en', 'fr'],
defaultLocale: 'en',
localeDetection: true,
},
}
//
// blog\[id]\[title]
//
export async function getStaticPaths() {
const response = await axios.get('https://api.myappi.com/blog')
const posts = response.data
const paths = posts.map((post: Props) => ({
params: { id: post.Id, title: post.Title },
}))
return { paths, fallback: false }
}
export async function getStaticProps(props: IStaticProps) {
const { id, locale } = props.params
const response = await axios.get(`https://api.myappi.com/blog/${id}`)
const post = await response.data
if (!post) {
return {
notFound: true,
}
}
return {
props: {
Id: post.Id,
Title: post.Title,
Blog: post.Blog,
DatePosted: post.DatePosted,
PostedBy: post.PostedBy,
...(await serverSideTranslations(props.locale, ['common', 'blog']))
}
}
}
For dynamic routes, you have to explicitly return the locales you want to be pre-generated from the getStaticPaths function. If you don't, Next.js will only generate pages for the default locale.
From Internationalized Routing documentation:
For pages using getStaticProps with Dynamic Routes, all locale
variants of the page desired to be prerendered need to be returned
from getStaticPaths. Along with the params object returned for
paths, you can also return a locale field specifying which locale
you want to render.
This can be achieved by modifying your getStaticPaths function to generate a path for each slug/locale combination.
export async function getStaticPaths({ locales }) { // Get available locales from `context`
const response = await axios.get('https://api.myappi.com/blog')
const posts = response.data
const paths = posts
.map((post: Props) => locales.map((locale) => ({
params: { id: post.Id, title: post.Title },
locale // Pass locale here
})))
.flat() // Flatten array to avoid nested arrays
return { paths, fallback: false }
}
I don't understand these errors when I export as production npm run build , but when I test npm run dev it works just fine. I use getStaticProps and getStaticPath fetch from an API route.
First when I npm run build
FetchError: invalid json response body at https://main-website-next.vercel.app/api/products reason: Unexpected token T in JSON at position
0
at D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:272:32
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async getStaticPaths (D:\zummon\Main Website\main-website-next\.next\server\pages\product\[slug].js:1324:18)
at async buildStaticPaths (D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:16:80)
at async D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:26:612
at async D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\tracer.js:1:1441 {
type: 'invalid-json'
}
\pages\product\[slug]
import { assetPrefix } from '../../next.config'
export default function Page(){...}
export const getStaticProps = async ({ params: { slug }, locale }) => {
const res = await fetch(`${assetPrefix}/api/products/${slug}`)
const result = await res.json()
const data = result.filter(item => item.locale === locale)[0]
const { title, keywords, description } = data
return {
props: {
data,
description,
keywords,
title
}
}
}
export const getStaticPaths = async () => {
const res = await fetch(`${assetPrefix}/api/products`)
const result = await res.json()
const paths = result.map(({ slug, locale }) => ({ params: { slug: slug }, locale }))
return {
fallback: true,
paths,
}
}
next.config.js
const isProd = process.env.NODE_ENV === 'production'
module.exports = {
assetPrefix: isProd ? 'https://main-website-next.vercel.app' : 'http://localhost:3000',
i18n: {
localeDetection: false,
locales: ['en', 'th'],
defaultLocale: 'en',
}
}
API routes
// pages/api/products/index.js
import data from '../../../data/products'
export default (req, res) => {
res.status(200).json(data)
}
// pages/api/products/[slug].js
import db from '../../../data/products'
export default ({ query: { slug } }, res) => {
const data = db.filter(item => item.slug === slug)
if (data.length > 0) {
res.status(200).json(data)
} else {
res.status(404).json({ message: `${slug} not found` })
}
}
// ../../../data/products (data source)
module.exports = [
{ locale: "en", slug: "google-sheets-combine-your-cashflow",
title: "Combine your cashflow",
keywords: ["Google Sheets","accounting"],
description: "...",
},
...
]
Second when I remove the production domain, I run npm run build but still get the error like
TypeError: Only absolute URLs are supported
at getNodeRequestOptions (D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:1305:9)
at D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:1410:19
at new Promise (<anonymous>)
at fetch (D:\zummon\Main Website\main-website-next\node_modules\node-fetch\lib\index.js:1407:9)
at getStaticPaths (D:\zummon\Main Website\main-website-next\.next\server\pages\[slug].js:938:21)
at buildStaticPaths (D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:16:86)
at D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\utils.js:26:618
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async D:\zummon\Main Website\main-website-next\node_modules\next\dist\build\tracer.js:1:1441 {
type: 'TypeError'
}
My next.config.js after remove
const isProd = process.env.NODE_ENV === 'production'
module.exports = { //remove
assetPrefix: isProd ? '' : 'http://localhost:3000',
i18n: {
localeDetection: false,
locales: ['en', 'th'],
defaultLocale: 'en',
}
}
My package.json when I npm run build script
{
"name": "main-website-next",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build && next export",
"start": "next start"
},
"dependencies": {
"next": "10.0.6",
"react": "17.0.1",
"react-dom": "17.0.1"
}
}
You should not call an internal API route inside getStaticProps. Instead, you can safely use your API logic directly in getStaticProps/getStaticPaths. These only happen server-side so you can write server-side code directly.
As getStaticProps runs only on the server-side, it will never run on
the client-side. It won’t even be included in the JS bundle for the
browser, so you can write direct database queries without them being
sent to browsers.
This means that instead of fetching an API route from
getStaticProps (that itself fetches data from an external source),
you can write the server-side code directly in getStaticProps.
Furthermore, your API routes are not available during build-time, as the server has not been started at that point.
Here's a small refactor of your code to address the issue.
// /pages/product/[slug]
import db from '../../../data/products'
// Remaining code..
export const getStaticProps = async ({ params: { slug }, locale }) => {
const result = db.filter(item => item.slug === slug)
const data = result.filter(item => item.locale === locale)[0]
const { title, keywords, description } = data
return {
props: {
data,
description,
keywords,
title
}
}
}
export const getStaticPaths = async () => {
const paths = db.map(({ slug, locale }) => ({ params: { slug: slug }, locale }))
return {
fallback: true,
paths,
}
}
I have a React.js and Next.js web site, it works well until I export production build. When I publish my project on the server, I get this problems: When I refresh the page it gives me 404 error.
I found Next.js 404 error after refreshing the page and set its configs. but my problem still exists.
All my links are like this:
import Link from 'next/link';
export default () => (
<Link href="/profile">
<a>profile</a>
</Link>
)
For static HTML export you must use exportPathMap
Refer: https://nextjs.org/docs/api-reference/next.config.js/exportPathMap
module.exports = {
exportPathMap: async function (
defaultPathMap,
{ dev, dir, outDir, distDir, buildId }
) {
return {
'/': { page: '/' },
'/profile': { page: '/profile' },
'/p/hello-nextjs': { page: '/post', query: { title: 'hello-nextjs' } },
'/p/learn-nextjs': { page: '/post', query: { title: 'learn-nextjs' } },
'/p/deploy-nextjs': { page: '/post', query: { title: 'deploy-nextjs' } },
}
},
}
Add this to next.config.js
module.exports = {
trailingSlash: true,
}