skip to Main Content

One of the requirements for an web app i’m working on is to have multiple languages, to implement this i’m using i18n and zustand to store the new set language and have it accessible in any component/page.

I have two select boxes, one in the navbar and another in the footer of the page to set language.
The problem is, when i set a new language in one select box, the page is translated, but i want the other select box to have it’s default value as the new language set(picking it up from zustand store), so in both the navbar and the footer would have the same default value and be synced whenever one has changed.

For example the page starts with portuguese, but if i change it to english, the other select box still has the old value(portuguese), altho it’s default value is set to the value in zustand store.

I’ve thought and tried couple solutions like storing the language in local storage and then get it or creating a function that runs always but none worked for me. Also wasn’t alble to find anyonw with a similiar problem when researching it.

Bellow follows the code for the navbar component, the footer component and the zustand store.

Navbar:

import {
  BtnWrapper,
  LanguageSelector,
  Link,
  Logo,
  NavBar,
  NavListItem,
  NavlinkItemHighlighted,
  Navlist,
  RegisterAndLoginButton,
} from "./NavbarWithoutLogin.styles";
import { useTranslation } from "react-i18next";
import { useWebSiteLanguageStore } from "../../../store/WebSiteLanguageStore";

export const Nav = () => {
  const { t } = useTranslation()
  const websiteLanguageStore = useWebSiteLanguageStore()

  return (
    <>
      <NavBar>
        <Logo>logo</Logo>
        <Navlist>
          <NavlinkItemHighlighted>
            <span>{t("navbarLink1")}</span>
          </NavlinkItemHighlighted>
          <NavListItem>
            <Link>{t("navbarLink2")}</Link>
          </NavListItem>
          <NavListItem>
            <Link>{t("navbarLink3")}</Link>
          </NavListItem>
          <NavListItem>
            <Link>{t("navbarLink4")}</Link>
          </NavListItem>
          <NavListItem>
            <Link>{t("navbarLink5")}</Link>
          </NavListItem>
          <NavListItem>
            <RegisterAndLoginButton>
              {t("navbarLoginButton")}
            </RegisterAndLoginButton>
            |
            <RegisterAndLoginButton>
              {t("navbarRegisterButton")}
            </RegisterAndLoginButton>
          </NavListItem>
        </Navlist>
        <BtnWrapper>
          <LanguageSelector
            defaultValue={websiteLanguageStore.currentlySelectedLanguage}
            onChange={(e) =>
              websiteLanguageStore.changeCurrentlySelectedLanguage(
                e.target.value
              )
            }
          >
            <option value="pt">{t("navbarLanguageSelectorOption1")}</option>
            <option value="fr">{t("navbarLanguageSelectorOption2")}</option>
            <option value="en">{t("navbarLanguageSelectorOption3")}</option>
            <option value="de">{t("navbarLanguageSelectorOption4")}</option>
            <option value="tt">{t("navbarLanguageSelectorOption5")}</option>
            <option value="es">{t("navbarLanguageSelectorOption6")}</option>
          </LanguageSelector>
        </BtnWrapper>
      </NavBar>
    </>
  );
};

Footer:

import {
  LinkBox,
  LinkBoxUlLi,
  Footer,
  Copyright,
  CopyrightText,
  HelpCenterBox,
  LanguageSelector,
} from "./PageFooter.styles";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCopyright } from "@fortawesome/free-solid-svg-icons";
import {
  faFacebook,
  faLinkedin,
  faSquareInstagram,
  faYoutube,
} from "@fortawesome/free-brands-svg-icons";
import { useTranslation } from "react-i18next";
import { useWebSiteLanguageStore } from "../../../store/WebSiteLanguageStore";

export const PageFooter = () => {
  const { t } = useTranslation();
  const websiteLanguageStore = useWebSiteLanguageStore();

  return (
    <>
      <Footer>
        <div>
          <b>{t("footerFistSectionTitle")}</b>
          <LinkBox>
            <LinkBoxUlLi>
              <a>{t("footerFirstSectionOptions1")}</a>
            </LinkBoxUlLi>
            <LinkBoxUlLi>
              <a>{t("footerFirstSectionOptions12")}</a>
            </LinkBoxUlLi>
            <LinkBoxUlLi>
              <a>{t("footerFirstSectionOptions13")}</a>
            </LinkBoxUlLi>
          </LinkBox>
        </div>
        <div>
          <b>{t("footerSecondSectionTitle")}</b>
          <LinkBox>
            <LinkBoxUlLi>
              <a>{t("footerSecondSectionOption")}</a>
            </LinkBoxUlLi>
            <LinkBoxUlLi>
              <a>{t("footerSecondSectionOption2")}</a>
            </LinkBoxUlLi>
            <LinkBoxUlLi>
              <a>{t("footerSecondSectionOption3")}</a>
            </LinkBoxUlLi>
            <LinkBoxUlLi>
              <a>{t("footerSecondSectionOption4")}</a>
            </LinkBoxUlLi>
          </LinkBox>
        </div>
        <div>
          <b>{t("footerThirdSectionTitle")}</b>
          <LinkBox>
            <LinkBoxUlLi>
              <a>{t("footerThirdSectionOption")}</a>
            </LinkBoxUlLi>
            <LinkBoxUlLi>
              <a>{t("footerThirdSectionOption2")}</a>
            </LinkBoxUlLi>
          </LinkBox>
        </div>
        <div>
          <b>{t("footerThirdSectionTitle")}</b>
          <LinkBox>
            <LinkBoxUlLi>
              <HelpCenterBox>
                {t("footerFourthSectionHelpCenterBox")}
              </HelpCenterBox>
            </LinkBoxUlLi>
          </LinkBox>
          <span>{t("footerFourthSectionLanguageSelector")}: </span>
          <LanguageSelector
            defaultValue={websiteLanguageStore.currentlySelectedLanguage}
            onChange={(e) => {
              websiteLanguageStore.changeCurrentlySelectedLanguage(
                e.target.value
              );
            }}
          >
            <option value="pt">{t("footerLanguageSelectorOption1")}</option>
            <option value="fr">{t("footerLanguageSelectorOption2")}</option>
            <option value="en">{t("footerLanguageSelectorOption3")}</option>
            <option value="de">{t("footerLanguageSelectorOption4")}</option>
            <option value="tt">{t("footerLanguageSelectorOption5")}</option>
            <option value="es">{t("footerLanguageSelectorOption6")}</option>
          </LanguageSelector>
        </div>
        <div>
          <b>{t("footerFollowUsSectionTitle")}</b>
          <LinkBox>
            <LinkBoxUlLi>
              <a>
                <FontAwesomeIcon icon={faFacebook} size="xl" />
              </a>{" "}
              <a>
                <FontAwesomeIcon icon={faSquareInstagram} size="xl" />
              </a>{" "}
              <a>
                <FontAwesomeIcon icon={faYoutube} size="xl" />{" "}
                <a>
                  <FontAwesomeIcon icon={faLinkedin} size="xl" />
                </a>{" "}
              </a>{" "}
            </LinkBoxUlLi>
          </LinkBox>
        </div>
      </Footer>
      <Copyright>
        <CopyrightText>
          <FontAwesomeIcon icon={faCopyright} /> {t("footerCopyWrightText")}{" "}
        </CopyrightText>
      </Copyright>
    </>
  );
};

zustand store:

import { create } from "zustand";
import i18n from "../i18n";

type WebSiteLanguageSettings = {
  currentlySelectedLanguage: string
  changeCurrentlySelectedLanguage: (language: string) => void
}

export const useWebSiteLanguageStore = create<WebSiteLanguageSettings>(
  (set) => {
    return {
      currentlySelectedLanguage: i18n.language,
      changeCurrentlySelectedLanguage(language) {
        i18n.changeLanguage(language)
        this.currentlySelectedLanguage = language
      },
    };
  }
);

I’m using styled-components, if the style files are needed i can post it here.

2

Answers


  1. I had never used zustand befor,but I saw it’s document ,if you lost the ‘set’ function?

    import { create } from 'zustand'
    
    const useBearStore = create((set) => ({
      bears: 0,
      increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
      removeAllBears: () => set({ bears: 0 }),
    }))
    Login or Signup to reply.
  2. To keep two select boxes in sync based on zustand store value in react, you need to use the same selector function for both of them and pass a shallow equality function as the second argument to the useStore hook.

    import create from 'zustand'
    import shallow from 'zustand/shallow'
    
    const useStore = create (set => ( {
      options: ['A', 'B', 'C', 'D'],
      selected: 'A',
      setSelected: value => set (state => ( { selected: value })),
    }))
    
    const selectOptions = state => ( { options: state.options, selected: state.selected })
    
    
    const SelectBox = () => {
      
      const { options, selected, setSelected } = useStore (selectOptions, shallow)
    
     
      const handleChange = event => {
        setSelected (event.target.value)
      }
    
    
      return (
        <select value={selected} onChange={handleChange}>
          {options.map (option => (
            <option key={option} value={option}>
              {option}
            </option>
          ))}
        </select>
      )
    }
    
    
    const App = () => {
      return (
        <div>
          <SelectBox />
          <SelectBox />
        </div>
      )
    }
    

    This code will render two select boxes that are always in sync with each other and the store.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search