i18next / next-i18next

The easiest way to translate your NextJs apps.
https://next.i18next.com
MIT License
5.57k stars 762 forks source link

i18next doesn't switch to other language, is stucked in the default one #514

Closed HectorLS closed 5 years ago

HectorLS commented 5 years ago

Describe the bug

i18next update doesnt update the text when switching to other language.

So the issue is i dont get any error. so i click to change language button expecting to see the text change, but i doesn't work. seems to be stuck in english on load i only get in the terminal a warning about namespaces:

You have not declared a namespacesRequired array on your page-level component: ErrorPage. This will cause all namespaces to be sent down to the client, possibly negatively impacting the performance of your app. For more info, see: https://github.com/isaachinman/next-i18next#4-declaring-namespace-dependencies

Locales

locales/en/common.json locales/es/common.json locales/de/common.json locales/fr/common.json

sample:

{
  "hello": "Hello",
  "language": {
    "english" : "english",
    "french" : "french",
    "german" : "german",
    "spanish" : "spanish"
  }
}

Occurs in next-i18next version

"next": "^9.0.1",
"next-i18next": "^1.2.1",

"engines": {
    "node": "^12.6.0",
    "npm": "^6.10.0",
    "yarn": "^1.17.0"
  },

OS

Files

i18n.js

const NextI18Next = require('next-i18next').default

module.exports = new NextI18Next({
  browserLanguageDetection: true,
  serverLanguageDetection:  true,
  defaultNS: 'common',
  defaultLanguage: 'en',
  ignoreRoutes: ['/_next/', '/static/'],
  otherLanguages: ['de, es, fr'],
  localeExtension:'json',
  localePath:   'locales',
  localeStructure: '{{lng}}/{{ns}}',
  localeSubpaths: {
    de: 'german',
    en: 'eng',
    es: 'esp',
    fr: 'french'
  }
})

 server.js

const express = require('express');
const next = require('next');
const nextI18NextMiddleware = require('next-i18next/middleware').default;

// i18n middleware setup options
const NextI18NextInstance = require('./i18n');

const port = parseInt(process.env.PORT || 3000, 10);
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

(async () => {
  await app.prepare();
  const server = express();

  // i18n middleware to handle multilingual app
  try {
    server.use(nextI18NextMiddleware(NextI18NextInstance))
  } catch (e) {
    throw (e)
  }

  // Handle nextjs routing
  server.get('*', (req, res) => handle(req, res));

  await server.listen(port);
  console.info(`> Ready on http://localhost:${port}`);
})();

pages/_app.tsx

import App from 'next/app'
import React from 'react'
import withRedux from 'next-redux-wrapper'
import { Provider } from 'react-redux'
import { ThemeProvider } from 'styled-components';

import i18n from '../i18n';

import { theme } from '../styles/reset';
import { initStore } from '../redux/store'

export interface AppProps {
  store: any
}

export interface AppState { }

class MyApp extends App<AppProps, AppState> {
  static async getInitialProps({ Component, ctx }) {

    const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {}

    return { pageProps }
  }

  render() {
    const { Component, pageProps, store } = this.props

    return (
      <ThemeProvider theme={theme}>
        <Provider store={store}>
          <Component {...pageProps} />
        </Provider>
      </ThemeProvider>
    )
  }
}

export default withRedux(initStore)(i18n.appWithTranslation(MyApp))

pages/index.tsx

import { Fragment } from 'react'
import { connect } from 'react-redux'

import i18next from '../i18n';

const IndexPage = ({ mediaType }) => {
  const { t, i18n } = i18next.useTranslation()

  return (
    <Fragment>
      <h1>{t('hello')}</h1>
      <p>{i18n.language}</p>
      <button type='button' onClick={() => i18n.changeLanguage('en')}>{t('language.spanish')}</button>
      <button type='button' onClick={() => i18n.changeLanguage('es')}>{t('language.spanish')}</button>
      <button type='button' onClick={() => i18n.changeLanguage('fr')}>{t('language.french')}</button>
      <button type='button' onClick={() => i18n.changeLanguage('de')}>{t('language.german')}</button>
    </Fragment>
  )
}

/* IndexPage.getInitialProps = () => {
  return {
    namespacesRequired: i18next.includeDefaultNamespaces
  };
}; */

const mapStateToProps = ({ browser }) => {
  const { mediaType } = browser

  return { mediaType }
}

const withRedux = connect(mapStateToProps)

export default withRedux(IndexPage)
isaachinman commented 5 years ago

Please provide a repository.

FMakareev commented 5 years ago

Describe the bug

i18next update doesnt update the text when switching to other language.

So the issue is i dont get any error. so i click to change language button expecting to see the text change, but i doesn't work. seems to be stuck in english on load i only get in the terminal a warning about namespaces

Locales

locales/en/common.json locales/es/common.json locales/de/common.json locales/fr/common.json

sample:

{
  "hello": "Hello",
  "language": {
    "english" : "english",
    "french" : "french",
    "german" : "german",
    "spanish" : "spanish"
  }
}

Occurs in next-i18next version

"next": "^9.0.1",
"next-i18next": "^1.2.1",

"engines": {
    "node": "^12.6.0",
    "npm": "^6.10.0",
    "yarn": "^1.17.0"
  },

OS

  • Device: MBP OS Mojave 10.14.6
  • Browser: Version 76.0.3809.132 (Official Build) (64-bit)

Files

i18n.js

const NextI18Next = require('next-i18next').default

module.exports = new NextI18Next({
  browserLanguageDetection:   true,
  serverLanguageDetection:    true,
  defaultNS: 'common',
  defaultLanguage: 'en',
  ignoreRoutes:   ['/_next/', '/static/'],
  otherLanguages: ['de, es, fr'],
  localeExtension:'json',
  localePath: 'locales',
  localeStructure: '{{lng}}/{{ns}}',
  localeSubpaths: {
    de: 'german',
    en: 'eng',
    es: 'esp'
    fr: 'french'
  }
})

 server.js

const express = require('express');
const next = require('next');
const nextI18NextMiddleware = require('next-i18next/middleware').default;

// i18n middleware setup options
const NextI18NextInstance = require('./i18n');

const port = parseInt(process.env.PORT || 3000, 10);
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

(async () => {
  await app.prepare();
  const server = express();

  // i18n middleware to handle multilingual app
  try {
    server.use(nextI18NextMiddleware(NextI18NextInstance))
  } catch (e) {
    throw (e)
  }

  // Handle nextjs routing
  server.get('*', (req, res) => handle(req, res));

  await server.listen(port);
  console.info(`> Ready on http://localhost:${port}`);
})();

pages/_app.tsx

import App from 'next/app'
import React from 'react'
import withRedux from 'next-redux-wrapper'
import { Provider } from 'react-redux'
import { ThemeProvider } from 'styled-components';

import i18n from '../i18n';

import { theme } from '../styles/reset';
import { initStore } from '../redux/store'

export interface AppProps {
  store: any
}

export interface AppState { }

class MyApp extends App<AppProps, AppState> {
  static async getInitialProps({ Component, ctx }) {

    const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {}

    return { pageProps }
  }

  render() {
    const { Component, pageProps, store } = this.props

    return (
      <ThemeProvider theme={theme}>
        <Provider store={store}>
          <Component {...pageProps} />
        </Provider>
      </ThemeProvider>
    )
  }
}

export default withRedux(initStore)(i18n.appWithTranslation(MyApp))

pages/index.tsx

import { Fragment } from 'react'
import { connect } from 'react-redux'

import i18next from '../i18n';

const IndexPage = ({ mediaType }) => {
  const { t, i18n } = i18next.useTranslation()

  return (
    <Fragment>
      <h1>{t('hello')}</h1>
      <p>{i18n.language}</p>
      <button type='button' onClick={() => i18n.changeLanguage('en')}>{t('language.spanish')}</button>
      <button type='button' onClick={() => i18n.changeLanguage('es')}>{t('language.spanish')}</button>
      <button type='button' onClick={() => i18n.changeLanguage('fr')}>{t('language.french')}</button>
      <button type='button' onClick={() => i18n.changeLanguage('de')}>{t('language.german')}</button>
    </Fragment>
  )
}

/* IndexPage.getInitialProps = () => {
  return {
    namespacesRequired: i18next.includeDefaultNamespaces
  };
}; */

const mapStateToProps = ({ browser }) => {
  const { mediaType } = browser

  return { mediaType }
}

const withRedux = connect(mapStateToProps)

export default withRedux(IndexPage)

Hi. In property localePath write path from the root project, static/locales.

HectorLS commented 5 years ago

Please provide a repository.

Hi @isaachinman , sorry the delay i had to created from the scratch to do a clean sample case

Repository yarn dev

isaachinman commented 5 years ago

Presumably, your language is not changing because the call to your locales destination is 404'ing, because you haven't put your locales inside the NextJs static dir.

I suggest you read the documentation carefully, and heed advice from fellow users like @FMakareev.

HectorLS commented 5 years ago

@isaachinman

I have read the documentation and i set the localePath to dont use the static folder, and use locales instead.

Anyway if i move the locales into a static folder and remove the localePath key to use the defaults. problem persist...

i18n.js

module.exports = new NextI18Next({
  browserLanguageDetection: true,
  serverLanguageDetection:  true,
  defaultNS: 'common',
  defaultLanguage: 'en',
  ignoreRoutes: ['/_next/', '/static/'],
  otherLanguages: ['de, es, fr'],
  localeExtension:'json',
  localePath:   'locales',     //  Problem persist if i comment this line and use ./static/locales
  localeStructure: '{{lng}}/{{ns}}',
  localeSubpaths: {
    de: 'german',
    en: 'eng',
    es: 'esp',
    fr: 'french'
  }
})
iedmrc commented 3 years ago

Hi @HectorLS , did you solve the issue? It seems it's a common problem. next-18next often doesn't load other languages except for the default one.

ziedHamdi commented 2 years ago

Presumably, your language is not changing because the call to your locales destination is 404'ing, because you haven't put your locales inside the NextJs static dir.

I suggest you read the documentation carefully, and heed advice from fellow users like @FMakareev.

The logs should write an error message if things like this happen, we cannot navigate in the dark to guess what it can be due to right?

For me, it is falling back to fallbackLng, if I change it to another value it always falls back to that language. Meaning that all languages are found by the lib, but for some reason not written in the logs, it falls back to fallbackLng

For this config:

const getUserLanguage = () => (typeof window === 'undefined') ? 'en' : window.navigator.userLanguage || window.navigator.language;
const userLanguage = getUserLanguage()
module.exports = {
    i18n: {
        debug: true,
        lng: `${userLanguage}`,
        fallbackLng: 'fr',
        defaultLocale: 'cz',
        interpolation: {
            escapeValue: false, // not needed for react as it escapes by default
        },
        locales: ['en', 'fr', 'cz', 'sk', 'it'],
    },
};

I get this (but italian never displays, it always shows 'fr' apart the first SSR display that is in english):

i18next::backendConnector: loaded namespace complaintList for language it {
  action: {
    sameIssue: 'Stesso problema',
    solved: 'risolto',
    quit: 'chiudi',
    like: 'mi piace',
    unlike: 'mi piace'
  },
  edit: {
    header: 'Modifica la tua richiesta',
    toast: {
      entityNotSelected: "Devi selezionare l'organizzazione per cui stai presentando la richiesta",
      titleMissing: 'Compila un titolo per la tua richiesta',
      descMissing: 'Per favore racconta la tua storia in modo che le persone possano mostrare empatia e migliorare la visibilità della tua richiesta agli altri',
      pictureMissing: "Aggiungendo un'immagine, le persone hanno il doppio delle probabilità di fare clic sulla tua richiesta. Se non hai una foto, scegline una che esprima i tuoi sentimenti dal web."
    },
    search: { placeholder: 'risolto0' },
    title: { label: 'risolto1', ph: 'risolto2' },
    desc: 'risolto3',
    saveDraft: 'risolto4',
    publish: 'risolto5',
    cancel: 'risolto6'
  },
  item: {
    header: {
      and: 'risolto7',
      others: 'risolto8',
      joined: 'risolto9',
      posted: 'chiudi0',
      aloneAgainst: 'chiudi1'
    },
    trending: { title: [Object] },
    rightPanel: {
      joinState: [Object],
      join: 'chiudi4',
      joinBack: 'chiudi5',
      agree: 'chiudi6',
      cancelAgree: 'chiudi7',
      edit: 'chiudi8',
      delete: 'chiudi9',
      solved: 'mi piace0',
      quit: 'mi piace1',
      solvedPercentage: 'mi piace0',
      solvedPercentageNone: 'mi piace2',
      solvedPercentageShort: 'mi piace0'
    },
    stats: {
      showSolidarity: 'chiudi6',
      comments: 'mi piace3',
      shared: 'mi piace4'
    },
    showMore: 'mi piace5',
    showLess: 'mi piace6'
  },
  detail: {
    header: { joined: 'mi piace7', viewed: 'mi piace8' },
    descLabel: 'risolto3',
    comments: { count: 'mi piace9', add: 'mi piace0' }
  },
  status: { WAITING_FOR_PROPOSAL: 'mi piace1', DRAFT: 'mi piace2' },
  companies: { mostViewed: 'mi piace3' }
}
i18next: languageChanged en
i18next: initialized {
  debug: true,
  initImmediate: undefined,
  ns: [ 'common', 'complaintList', 'entitySearch', 'header', 'landing' ],
  defaultNS: 'common',
  fallbackLng: [ 'fr' ],
  fallbackNS: false,
  whitelist: false,
  nonExplicitWhitelist: false,
  supportedLngs: false,
  nonExplicitSupportedLngs: false,
  load: 'currentOnly',
  preload: [ 'en', 'fr', 'cz', 'sk', 'it' ],
  simplifyPluralSuffix: true,
  keySeparator: '.',
  nsSeparator: ':',
  pluralSeparator: '_',
  contextSeparator: '_',
  partialBundledLanguages: false,
  saveMissing: false,
  updateMissing: false,
  saveMissingTo: 'fallback',
  saveMissingPlurals: true,
  missingKeyHandler: false,
  missingInterpolationHandler: false,
  postProcess: false,
  postProcessPassResolved: false,
  returnNull: true,
  returnEmptyString: true,
  returnObjects: false,
  joinArrays: false,
  returnedObjectHandler: false,
  parseMissingKeyHandler: false,
  appendNamespaceToMissingKey: false,
  appendNamespaceToCIMode: false,
  overloadTranslationOptionHandler: [Function: handle],
  interpolation: { escapeValue: false },
  errorStackTraceLimit: 0,
  localeExtension: 'json',
  localePath: './public/locales',
  localeStructure: '{{lng}}/{{ns}}',
  react: { useSuspense: true },
  reloadOnPrerender: false,
  serializeConfig: true,
  strictMode: true,
  use: [],
  default: {
    i18n: {
      debug: true,
      lng: 'en',
      fallbackLng: 'fr',
      defaultLocale: 'cz',
      interpolation: [Object],
      locales: [Array]
    }
  },
  lng: 'en',
  defaultLocale: 'cz',
  locales: [ 'en', 'fr', 'cz', 'sk', 'it' ],
  backend: {
    addPath: '/home/oem/work/projects/weally.org/public/locales/{{lng}}/{{ns}}.missing.json',
    loadPath: '/home/oem/work/projects/weally.org/public/locales/{{lng}}/{{ns}}.json',
    ident: 2,
    parse: [Function: parse],
    stringify: [Function: stringify]
  },
  ignoreJSONStructure: true
}

The strange thing is that I have these logs in by backend server while I'm not in SSR, just changing language on client side

Umamad commented 1 year ago

Same here!

all of namespaces loads in default language ( en-GB ) and works fine but in the other one ( ta-IN ) some of namespaces doesn't load.

for example one of my pages wich is SSR and used ( check the last file ) in this page /area/[area].js the namespace area_page just loads in en-GB but not in ta-IN. en-GB is default locale.

i tried reach the json file using url both of below urls return 200 {localhost}/en-GB/locales/en-GB/area_page.json {localhost}/en-GB/locales/ta-IN/area_page.json but {localhost}/ta-IN/locales/en-GB/area_page.json {localhost}/ta-IN/locales/ta-IN/area_page.json give 404

i'm just confused

I'm using Next js with next-i18next.

translation files

Screenshot 2023-04-07 at 1 58 10 PM Screenshot 2023-04-07 at 2 02 19 PM

package.json

"next": "^12.3.1",
"next-i18next": "^12.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",

next.config.js

const { i18n } = require("./next-i18next.config");

module.exports = {
  reactStrictMode: true,
  poweredByHeader: false,
  i18n,
  async rewrites() {
    return [
      {
        source: "/apps",
        destination: "/Apps",
      },
    ];
  },
};

next-i18next.config.js

module.exports = {
  debug: process.env.NODE_ENV === "development",
  i18n: {
    locales: ["en-GB", "ta-IN"],
    defaultLocale: "en-GB",
  },
  localePath:
    typeof window === "undefined"
      ? require("path").resolve("./public/locales")
      : "/locales",
  react: { useSuspense: true },
  reloadOnPrerender: process.env.NODE_ENV === "development",
};

_app.js

import { useEffect } from "react";
import PropTypes from "prop-types";
import Head from "next/head";
import Script from "next/script";
import { ThemeProvider } from "@mui/material/styles";
import CssBaseline from "@mui/material/CssBaseline";
import { CacheProvider } from "@emotion/react";
import createEmotionCache from "../src/createEmotionCache";
import NextNProgress from "nextjs-progressbar";
import { appWithTranslation } from "next-i18next";
import { setCookie } from "cookies-next";
import { useRouter } from "next/router";

// import nextI18NextConfig from "../next-i18next.config.js";

import theme from "../src/themes/mealzoTheme";

//! Custom Contexts And Providers
import AuthProvider from "../context/AuthContext";
import OrderProvider from "../context/OrderContext";
import PostCodeAddressProvider from "../context/PostCodeAndAddressContext";
import NotificationProvider from "../context/NotificationContext";

import { cookiesNameSpaces } from "../utilities/appWideHelperFunctions";

import "../src/css/StaticCss.styles.css";

// Client-side cache, shared for the whole session of the user in the browser.
const clientSideEmotionCache = createEmotionCache();

function MyApp(props) {
  const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;

  const router = useRouter();
  const { locale } = router;

  useEffect(() => {
    setCookie(cookiesNameSpaces.LOCALE, locale);
  }, [locale]);

  return (
    <CacheProvider value={emotionCache}>
      <Head>
        <link type="image/x-icon" rel="shortcut icon" href="/Images/logo.png" />
        <meta
          name="viewport"
          content="initial-scale=1, width=device-width, maximum-scale=1.0, user-scalable=0"
        />
        <meta property="og:url" content={process.env.HOST} />
        <meta property="og:type" content="website" />
        <meta itemProp="image" content="/Images/header.jpg" />
        <meta property="og:image" content="/Images/header.jpg" />
        <meta name="twitter:card" content="/Images/header.jpg" />
        <meta name="twitter:image" content="/Images/header.jpg" />
      </Head>
      <ThemeProvider theme={theme}>
        {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
        <CssBaseline />
        <NextNProgress
          color={theme.palette.primary.main}
          height={3}
          showOnShallow={false}
          options={{ showSpinner: false }}
        />

        <NotificationProvider>
          <AuthProvider>
            <PostCodeAddressProvider>
              <OrderProvider>
                <Component {...pageProps} />
              </OrderProvider>
            </PostCodeAddressProvider>
          </AuthProvider>
        </NotificationProvider>
      </ThemeProvider>
    </CacheProvider>
  );
}

export default appWithTranslation(MyApp);

MyApp.propTypes = {
  Component: PropTypes.elementType.isRequired,
  emotionCache: PropTypes.object,
  pageProps: PropTypes.object.isRequired,
};

area/[area].js

import { useState, Suspense, useMemo, useEffect } from "react";
import Head from "next/head";
import dynamic from "next/dynamic";
import { useRouter } from "next/router";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import useLocation from "../../hooks/useLocation";

import { Grid, NoSsr, Skeleton } from "@mui/material";

//! direct Imported Components
import AppBarLoading from "../../src/loadings/AppBar.loadings";
import AreaPageLoading from "../../src/loadings/AreaPage.loadings";
import MainFooterLoading from "../../src/loadings/MainFooter.loadings";

//! without suspense
const AreaDeliveryModal = dynamic(() =>
  import("../../src/modals/AreaDelivery.modals")
);
const PageContainer = dynamic(() =>
  import("../../src/containers/Page.containers")
);
const AreaFiltersModal = dynamic(() =>
  import("../../src/modals/AreaFilters.modals")
);

//! with suspense
const AreaFiltersSideBarComponent = dynamic(
  () => import("../../src/sidebars/AreaFilters.sidebars"),
  {
    suspense: true,
  }
);
const AreaCategoriesCarouselComponent = dynamic(
  () => import("../../src/carousels/AreaCategories.carousels"),
  {
    suspense: true,
  }
);
const AreaOptionsCarouselComponent = dynamic(
  () => import("../../src/carousels/AreaOptions.carousels"),
  {
    suspense: true,
  }
);
const AreaTopPicksCarouselComponent = dynamic(
  () => import("../../src/carousels/AreaTopPicks.carousels"),
  {
    suspense: true,
  }
);
const AreaFoodsListComponent = dynamic(
  () => import("../../src/lists/AreaFoods.lists"),
  {
    suspense: true,
  }
);
const AppBarComponent = dynamic(
  () => import("../../src/appbars/Area.appbars"),
  {
    suspense: true,
  }
);
const MainFooterComponent = dynamic(
  () => import("../../src/footers/Main.footers"),
  {
    suspense: true,
  }
);

import { AreaPageContext } from "../../context/AreaPageContext";
import {
  getCategoriesInitialState,
  queryParamDecode,
  organizeAreaInitialData,
  getFilterInitialState,
  saleMethods,
  queryKeyWords,
  getPrevQueryParamsInString,
  objectKeyValueToUrlQueryParam,
  areaHeadPropertiesHandler,
  saleMethodAndSearchChangeHandler,
  getLatLangAndSetCookieOnServer,
} from "../../utilities/areaPathHandlers";
import parseCookies from "../../utilities/parseCookies";
import axiosConfig, { localeHeaderConfig } from "../../config/axios";
import {
  cookiesNameSpaces,
  validateSaleMethod,
} from "../../utilities/appWideHelperFunctions";

const { LOCATION_DETAIL } = cookiesNameSpaces;

export default function Area({
  headData,
}) {
  // Get header data
  const router = useRouter();
  const { title, description, pageKeywords } = headData;
  const { postCode, updateLocationDetail } = useLocation(); 

  return (
    <>
      <Head>
        <title>{title}</title>

        <meta name="description" content={description} />
        <meta name="keywords" content={pageKeywords} />
        <meta itemProp="name" content={title} />
        <meta property="og:title" content={title} />
        <meta name="twitter:title" content={title} />
        <meta itemProp="description" />
        <meta property="og:description" />
        <meta name="twitter:description" />
      </Head>

        <PageContainer
          sx={{
            height: `${filterOpen ? "100vh" : "unset"}`,
            overflowY: `${filterOpen ? "hidden" : "unset"}`,
            mb: 3,
          }}
        >
          <Suspense fallback={<AppBarLoading />}>
            <NoSsr>
              <AppBarComponent />
            </NoSsr>
          </Suspense>
          <Grid
            container
            mt={{ md: 2, xs: 5 }}
            px={{ sm: 4, xs: 2 }}
            flexDirection="row"
            flexWrap="nowrap"
          >
            <Grid
              item
              xl={2.2}
              lg={3}
              md={4}
              xs={0}
              sx={{ display: { xs: "none", md: "block" }, pr: 5 }}
            >
              <Suspense
                fallback={
                  <Skeleton
                    variant="rectangular"
                    animation="wave"
                    width="100%"
                    height="100%"
                  />
                }
              >
                <AreaFiltersSideBarComponent />
              </Suspense>
            </Grid>
            <Grid
              item
              container
              flexDirection='column'
              xl={9.8}
              lg={9}
              md={8}
              xs={12}
              pt={{ xs: 5, md: 0 }}
            >
              <Suspense fallback={<AreaPageLoading />}>
                <AreaCategoriesCarouselComponent />
                <AreaOptionsCarouselComponent />
                <AreaTopPicksCarouselComponent />
                <AreaFoodsListComponent />
              </Suspense>
            </Grid>
          </Grid>
        </PageContainer>

      <Suspense fallback={<MainFooterLoading />}>
        <MainFooterComponent />
      </Suspense>
    </>
  );
}

export async function getServerSideProps(context) {
  const { req, res, query, locale } = context;

  // Calculate Page Title & Description
  const [title, description, pageKeywords] = areaHeadPropertiesHandler(
    postCode,
    cuisineCount,
    pageData,
    initialRequestParameters
  );

  return {
    props: {
      headData: {
        title,
        description,
        pageKeywords,
      },
      ...(await serverSideTranslations(locale, [
        "common",
        "area_page",
        "footer",
        "login_form",
        "sign_up_form",
        "drawer",
        "addresses",
      ])),
    },
  };
}

the component

import { useState, useContext, useEffect } from "react";
import { useRouter } from "next/router";
import { useTranslation } from "next-i18next";
//MUI
import { Checkbox, Typography, Box } from "@mui/material";
import KeyboardArrowDownRoundedIcon from "@mui/icons-material/KeyboardArrowDownRounded";
import RadioGroup from "@mui/material/RadioGroup";
import FormControlLabel from "@mui/material/FormControlLabel";

//custom MUI Styled
import {
  FiltersContainer,
  FiltersTitle,
  FiltersSort,
  FilterSortTitle,
  FilterSortContent,
  CustomRadioBtn,
  FiltersOffers,
  FilterOffersContent,
  CustomRadioLabel,
  FiltersCategory,
  CheckBoxGroup,
  FilterCategoriesContent,
  CustomFiltersButton,
} from "./AreaFilters.sidebars.styles";

import { AreaPageContext } from "../../context/AreaPageContext";
import {
  areaCategoryChangeHandler,
  areaFilterChangeHandler,
  areaFilterAndSearchReset,
} from "../../utilities/areaPathHandlers";

const AreaFiltersSideBar = () => {
  const { t } = useTranslation("area_page");

  //filter expands toggle
  const [sortOpen, setSortOpen] = useState(false);
  const [offerOpen, setOfferOpen] = useState(true);
  const [categoryOpen, setCategoryOpen] = useState(true);

  // Order: 1. Data   2. State   3. SetState
  const {
    // Category Group
    categoriesData,
    categories,
    setCategories,
    categoryQueryKeyWord,

    // Sort Group
    sortData,
    sort,
    setSort,
    sortQueryKeyWord,

    // Offers Group
    offersData,
    offer,
    setOffer,
    offerQueryKeyWord,

    // Shop Group
    setShops,
    shopLoading,
    setShopLoading,

    setFilterOpen,
  } = useContext(AreaPageContext);

  const router = useRouter();

  // categories Change Handler
  const onChangeCategories = (e) => {
    areaCategoryChangeHandler(
      router,
      e.target.value,
      e.target.name,
      categories,
      setCategories,
      categoryQueryKeyWord,
      setShops,
      shopLoading,
      setShopLoading
    );
  };

  // sort Change handler
  const onChangeSort = (e) => {
    areaFilterChangeHandler(
      router,
      sortQueryKeyWord,
      parseInt(e.target.value),
      setSort,
      sortData,
      setShops,
      shopLoading,
      setShopLoading
    );
  };

  // offers Change handler
  const onChangeOffer = (e) => {
    areaFilterChangeHandler(
      router,
      offerQueryKeyWord,
      parseInt(e.target.value),
      setOffer,
      offersData,
      setShops,
      shopLoading,
      setShopLoading
    );
  };

  return (
    <FiltersContainer>
      <Box
        sx={{
          overflowY: "auto",
          "&::-webkit-scrollbar": {
            width: "8px",
            marginRight: "5px",
          },
          "&::-webkit-scrollbar-thumb": {
            borderRadius: "10px",
            background: "#c0c2c269",
          },
          pr: 1,
        }}
      >
        <FiltersTitle>{t("area_filters_header")}</FiltersTitle>
        <FiltersSort>
          <FilterSortTitle onClick={() => setSortOpen(!sortOpen)}>
            <div>
              <KeyboardArrowDownRoundedIcon
                fontSize="10px"
                sx={{
                  transform: `rotate(${sortOpen ? "180deg" : "0"})`,
                  transition: "transform 0.25s ease-in",
                  mr: 1,
                }}
              />
              {t("area_filters_sort")}
            </div>

            <Typography
              sx={{ cursor: "pointer" }}
              fontSize="small"
              color="primary.main"
              onClick={(e) => {
                e.stopPropagation();
                areaFilterAndSearchReset(
                  router,
                  sortQueryKeyWord,
                  setSort,
                  setShops,
                  shopLoading,
                  setShopLoading
                );
              }}
            >
              {t("area_filters_reset")}
            </Typography>
          </FilterSortTitle>

          <FilterSortContent sortOpen={sortOpen}>
            <RadioGroup name="sort" value={sort} onChange={onChangeSort}>
              {sortData.map(({ id, title }) => (
                <FormControlLabel
                  sx={{
                    fontSize: "12px",
                  }}
                  value={id}
                  control={<CustomRadioBtn size="small" />}
                  label={<CustomRadioLabel>{title}</CustomRadioLabel>}
                  key={id}
                />
              ))}
            </RadioGroup>
          </FilterSortContent>
        </FiltersSort>

        {/* Offer Filters */}
        <FiltersOffers>
          <FilterSortTitle onClick={() => setOfferOpen(!offerOpen)}>
            <div>
              <KeyboardArrowDownRoundedIcon
                fontSize="10px"
                sx={{
                  transform: `rotate(${offerOpen ? "180deg" : "0"})`,
                  transition: "transform 0.25s ease-in",
                  mr: 1,
                }}
              />
              {t("area_filters_offers")}
            </div>

            <Typography
              sx={{ cursor: "pointer" }}
              fontSize="small"
              color="primary.main"
              onClick={(e) => {
                e.stopPropagation();
                areaFilterAndSearchReset(
                  router,
                  offerQueryKeyWord,
                  setOffer,
                  setShops,
                  shopLoading,
                  setShopLoading
                );
              }}
            >
              {t("area_filters_reset")}
            </Typography>
          </FilterSortTitle>
          <FilterOffersContent offerOpen={offerOpen}>
            <RadioGroup
              aria-labelledby="offers-controlled-radio-buttons-group"
              name="offers"
              value={offer}
              onChange={onChangeOffer}
            >
              {offersData.map(({ id, title, countOfOffer }) => (
                <FormControlLabel
                  value={id}
                  control={<CustomRadioBtn size="small" />}
                  label={
                    <CustomRadioLabel>
                      <span>{title}</span>
                      <span>({countOfOffer})</span>
                    </CustomRadioLabel>
                  }
                  key={id}
                />
              ))}
            </RadioGroup>
          </FilterOffersContent>
        </FiltersOffers>

        {/* Category Filters */}
        <FiltersCategory>
          <FilterSortTitle onClick={() => setCategoryOpen(!categoryOpen)}>
            <div>
              <KeyboardArrowDownRoundedIcon
                fontSize="10px"
                sx={{
                  transform: `rotate(${categoryOpen ? "180deg" : "0"})`,
                  transition: "transform 0.25s ease-in",
                  mr: 1,
                }}
              />
              {t("area_filters_categories")}
            </div>
            <Typography
              sx={{ cursor: "pointer" }}
              fontSize="small"
              color="primary.main"
              onClick={(e) => {
                e.stopPropagation();
                areaFilterAndSearchReset(
                  router,
                  categoryQueryKeyWord,
                  setCategories,
                  setShops,
                  shopLoading,
                  setShopLoading,
                  categoriesData
                );
              }}
            >
              {t("area_filters_reset")}
            </Typography>
          </FilterSortTitle>

          <FilterCategoriesContent
            categoryOpen={categoryOpen}
            itemsCount={categoriesData.length}
          >
            <CheckBoxGroup>
              {categoriesData.map((category, index) => (
                <FormControlLabel
                  value={category.id}
                  name={category.title}
                  control={
                    <Checkbox
                      size="small"
                      sx={{ color: "checkbox" }}
                      checked={categories[category.id]}
                    />
                  }
                  label={
                    <CustomRadioLabel>
                      <span>{category.title}</span>
                      <span>({category.countOfSubject})</span>
                    </CustomRadioLabel>
                  }
                  onChange={onChangeCategories}
                  key={index}
                />
              ))}
            </CheckBoxGroup>
          </FilterCategoriesContent>
        </FiltersCategory>
      </Box>
      <Box
        sx={{
          padding: "16px",
          bottom: 0,
          boxShadow: "0 1px 4px #00000029",
          marginTop: "auto",
          position: "sticky",
          display: { xs: "block", md: "none" },
          backgroundColor: "#fff",
        }}
      >
        <CustomFiltersButton onClick={() => setFilterOpen(false)}>
          {t("area_filters_done")}
        </CustomFiltersButton>
      </Box>
    </FiltersContainer>
  );
};

export default AreaFiltersSideBar;
allanortiz commented 1 year ago

did you solve the issue?

Umamad commented 1 year ago

did you solve the issue?

not yet

DeabitTech commented 2 weeks ago

hello, it does't work for me too. it's wired because if i put in a sidebar the language selector which is a siple dropdown with changeLanguage func from i18n, it work, but if i put the component into a children of a page managed by react router dom, it change the language but from "t" function dosen't have the value of different language, please help me