Next.js middleware get available locales - next.js

i created i18n.json
i18n.json
{
"locales": ["en-US", "tr-TR"],
"defaultLocale": "tr-TR",
"defaultNS": "common",
"pages": {
"*": ["common"]
}
}
i want to get locales ["en-US", "tr-TR"] values in _middleware.tsx
i created _middleware.tsx and
I have read the directions in this document
https://nextjs.org/docs/api-reference/next/server#nextrequest
nextUrl includes i18n
so i want to reach locales in i18n object but I get undefined value.
import { NextResponse, NextRequest } from "next/server";
export async function middleware(req: NextRequest) {
const { i18n } = req.nextUrl;
const { locales } = i18n;
console.log(locales);
return NextResponse.next();
}
How can I get the available locales values?

I'm not quite sure why you need middleware for this case, you can get all locales from the SSR props, but you need to configure
First, you need to define your locales in next-18next.config.js
module.exports = {
i18n: {
defaultLocale: "de",
locales: ["de", "en"],
},
};
Then in next page get all locales with SSR Props
export const getServerSideProps: GetServerSideProps = async ({ locales }) => {
console.log(locales) // You should get an array of all locales
}

For now, best way to do this is importing next-i18next.config.js manually.
next-i18next.config.js
module.exports = {
i18n: {
defaultLocale: "en",
locales: ["en", "tr"],
localeDetection: false,
},
};
middleware.js
import { i18n } from "./next-i18next.config.js";
export function middleware(req) {
const availableLocales = i18n.locales;
}
You can import your i18n.json to achieve same.

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.

next-i18next fail to load locales from public folder

I migrated to next.js and started to use next-i18next. I would like to know, how to make next-i18next load locales from the public folder? It works only when I place the locales folder in the root directory not in public.
i18n.json ;
{
"locales": ["en", "fr", "ar"],
"defaultLocale": "en",
"pages": {
"*": ["common"]
}
}
next.config.js ;
// next.config.js
const nextTranslate = require("next-translate");
module.exports = {
...nextTranslate(),
};
Create i18n.js file root add this code inside:
const path = require('path');
module.exports = {
i18n: {
locales: ['en', 'ru', 'tm'],
defaultLocale: 'en',
localeDetection: false,
localePath: path.resolve('./public/locales'),
},
};
and add config next.config.js file this:
const { i18n } = require('./i18n');
const nextConfig = {
reactStrictMode: true,
i18n,
};
module.exports = nextConfig;
after every page add transalition code. example: index.file
import React, { useContext, useEffect } from 'react';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useTranslation } from 'next-i18next';
import Router from 'next/router';
import { Title } from '../components';
import i18n from '../i18n';
const Home = () => {
const { t } = useTranslation('');
useEffect(() => {
if (!user) {
Router.push('/login');
}
});
return (
<>
<Title title={`${t('page.home')} | Trillo`} />
<div className="min-h-screen">Home</div>
</>
);
};
export const getServerSideProps = async ({ locale }) => (
{ props: {
...(await serverSideTranslations(
locale,
['common'],
i18n,
)),
} }
);
export default Home;
After you create these files inside public folder like this. see this and see this 2

Remove language from URL in Next JS dynamic routes

I have a website in NextJS with next-i18next, and every page must be translated except for the legal pages.
These legal pages are in markdown, and I have dynamic routing enabled thanks to a [legal].js page and the getStaticPaths and getStaticProps in it.
The problem is that by building my website, my legal pages are prefixed with the language (here en). I would like to remove it as I don't want these pages to be translated.
What am I doing wrong here?
Thanks a lot 🙏
Folder structure:
pages
|- index.js
|- [legal].js
|- privacy-policy.mdx
next-i18next.config.js
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en'],
fallbackLng: 'en',
defaultNS: ['homepage', 'header', 'footer'],
localeDetection: false,
},
}
[legal].js
import matter from 'gray-matter'
import ReactMarkdown from 'react-markdown'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import glob from 'glob'
const LegalPage = ({ markdownBody }) => (
<ReactMarkdown>{markdownBody}</ReactMarkdown>
)
export async function getStaticProps({ locale, params }) {
const { legal } = params
const content = await import(`./${legal}.mdx`)
const data = matter(content.default)
return {
props: {
...(await serverSideTranslations(locale, ['header', 'footer'])),
markdownBody: data.content,
},
}
}
export async function getStaticPaths() {
const blogs = glob.sync('src/pages/**/*.mdx')
const blogSlugs = blogs.map(file => {
const parts = file.split('/')
return parts[parts.length - 1].replace('.mdx', '')
})
const paths = blogSlugs.map(slug => ({
params: { legal: slug },
}))
return {
paths,
fallback: false,
}
}
export default LegalPage
build output:
All static HTML files should be created in separate folders for each locale.
Your default locale can be omitted in the URL, and the following URLs are equivalent:
example.com/my-first-blog
example.com/en/my-first-blog
🎈 As a solution, you can setup app i18n base on domain:
module.exports = {
i18n: {
locales: ['en'],
defaultLocale: 'en',
domains: [
{
domain: 'example.com',
defaultLocale: 'en',
},
]
}
}

next-i18next is not working with serversideprops in dynamic pages after deploying on vercel

I'm using next-i18next module for multilingual support.
I have some static pages and dynamic pages as well. both working fine on local.
I deployed all static pages on vercel, all worked fine on vercel. But dynamic page is not working on vercel. it shows 404 page for that dynamic page.
Below is the code of the dynamic page. (pages/test-page/[questionId].js)
import { useState, useEffect } from "react";
import {Layout} from "#components/common";
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { TestComponent } from '#components/TestComponent'
const TestPage = () =>
{
const { t } = useTranslation('common')
const router = useRouter()
const {questionId} = router.query;
const [isApiLoaded,setIsApiLoaded] = useState(false)
return (
<TestComponent
t={t}
isApiLoaded={isApiLoaded}
setIsApiLoaded={setIsApiLoaded}
/>
)
}
TestPage.Layout = Layout
export const getServerSideProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, ['home', 'common']))
}
});
export default TestPage;
How to fix this issue?
Adding localePath in next-i18next.config.js did help in my case.
const path = require('path')
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'de'],
localePath: path.resolve('./public/locales')
}
};
I was facing the same issue and for a temporary fix I used the i18n object from next-i18next that has a function called getResource that gets the current locale with its translations
// import { i18n } from 'next-i18next';
// import { useRouter } from 'next/router';
const [translation, setTranslation] = useState({});
useEffect(() => {
const bundle = i18n.getResource(locale, 'common');
setTranslation(bundle);
}, []);
And to avoid rewrite the code with the t function, you could use
// LINK https://stackoverflow.com/a/43849204/14263138
const t = (word) => word
.split('.')
.reduce((p, c) => (p && p[c]) || null, translation);
With this applied, you don't need to use the getServerSideProps
Although the post is now old, I share the solution that solved the problem in my project (focus on the addition of localePath):
const path = require('path');
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'it', 'de', 'es', 'fr', 'ja']
},
defaultNs: 'shared',
fallbackLng: { default: ['en', 'it', 'de', 'es', 'fr', 'ja'] },
localePath: path.resolve('./public/locales'),
};
I specify that localePath should not be included among the properties of i18n as indicated in another answer as doing so produces a type error.
Also make sure to use getServerSideProps and not getStaticProps on pages, for example:
export async function getServerSideProps({ locale }) {
return {
props: {
...(await ssrTranslations(locale, ['login', 'shared'])),
},
};
}
Import the serverSideTranslations
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
Now from the getServerSideProps, pass the ..(await serverSideTranslations(locale, ["common"])), with the props.
export const getServerSideProps: GetStaticProps = async ({
locale,
locales,
query
}: any) => {
return {
props: {
...(await serverSideTranslations(locale, ["common"])),
}
}
};
Now add your language strings inside
/public/locales/en/common.json
For example
{
"home": {
"Test": "Test"
}
}
You can add more language strings inside the locales directory.
Restart your Next.js app. It will work.

next-translate is returning key

Next translation is displaying key instead of lang
common:menu.1.title common:read_more
i18n.js
module.exports = {
locales: ['en', 'de', 'cs'],
defaultLocale: 'en',
redirectToDefaultLang: true,
pages: {
'*': ['common'],
'/404': ['home'],
'/': ['home'],
'/about': ['about'],
},
interpolation: {
prefix: '${',
suffix: '}',
},
logger: true,
logBuild: true,
loadLocaleFrom: (locale, namespace) =>
import(`./public/locales/${locale}/${namespace}`).then((m) => m.default),
}
this is my next.config.js
const nextTranslate = require('next-translate')
module.exports = nextTranslate()
_app.js
import I18nProvider from 'next-translate/I18nProvider';
class MyApp extends App {
render () {
const { Component, pageProps, store } = this.props
return (
<I18nProvider lang={'en'} >
<Component {...pageProps} />
<GoTop scrollStepInPx="50" delayInMs="16.66" />
</I18nProvider>
);
}
}
export default MyApp
and following HOC
import React, {Component} from "react";
import withTranslation from 'next-translate/withTranslation'
class NavBarLink extends Component {
render() {
const { t, lang } = this.props.i18n
const description = t('menu.1.title')
const description2 = t('read_more')
return <p>{description + ' '+ description2}</p>
}
}
export default withTranslation(NavBarLink, 'common')
The return value is common:menu.1.title common:read_more
Please can someone tell me what is missing in my code?
In new version of next-translate wouldn't need to provide I18nProvider as said here.
But when you provider that you should provide all namespaces you wanna use.
You'd better to look at this migration guide.

Resources