1

I'm trying to understand 'useContext' to translate different Components of my application. In my code I'm receiving the error "language, languages, and handleIdiom is not defined". I understand that in my "Menu" Component I didn't define them, but in the "Languages" Component that it is my provider, I defined all.

I thought since my "Menu" is wrapper with , I could use it. Also, could you please look at the code and tell me what is wrong and what changes are required to achieve my goal?

Context:

import React, { createContext, useState, useContext } from "react";

export const languages = {
  en: {
    about: "about",
    project: "project",
    contact: "contact",
    slogan: "Think the Design, and I design the Code.",
    button: "Learn more",
  },
  ptbr: {
    about: "sobre",
    project: "projetos",
    contact: "contatos",
    slogan: "Think the Design, and I design the Code.",
    button: "Learn more",
  },
  jp: {
    about: "nihon",
    project: "nihon",
    contact: "nihon",
    slogan: "nihon",
    button: "nihon",
  },
  ru: {
    about: "руский",
    project: "руский",
    contact: "руский",
    slogan: "руский",
    button: "руский",
  },
};

export const LanguagesContext = React.createContext({ languages });

function Language() {
  const [idiom, setIdiom] = useState("en");

  const handleIdiom = (language) => (e) => {
    setIdiom(language);
  };

  return (
    <LanguagesContext.Provider
      value={{ idiom, setIdiom }}
    ></LanguagesContext.Provider>
  );
}

export function useIdiom() {
  const context = useContext(LanguagesContext);
  const { idiom, setIdiom } = context;
  return { idiom, setIdiom };
}

export default Language;

Menu:

import React, { useState, useContext } from "react";
import "./MenuStyle.scss";
import { FaBars } from "react-icons/fa";
import br from "../../assets/menu/brazil.svg";
import en from "../../assets/menu/eua.svg";
import ru from "../../assets/menu/russia.svg";
import jp from "../../assets/menu/japan.svg";

import { useIdiom } from "../../provider/Language.jsx";

import LanguagesProvider from "../../provider/Language";

function Menu() {
  // burger menu
  const [click, setClick] = useState(false);
  const handleClick = () => setClick(!click);
  const Close = () => setClick(false);

  // tradution
  const idiom = useIdiom();

  return (
    <LanguagesProvider>
      <div>
        <div
          className={click ? "main-container" : ""}
          onClick={() => Close()}
        />

        <nav className="navbar" onClick={(e) => e.stopPropagation()}>
          <div className="nav-container">
            <div exact to="/" className="nav-logo">
              <h1>Flávio</h1>
            </div>

            <div className="links">
              <ul className={click ? "nav-menu active" : "nav-menu"}>
                <li className="nav-item">
                  <a href="" activeClassName="active" className="nav-links">
                    {languages[language].about}
                  </a>
                </li>
                <li className="nav-item">
                  <a href="" activeClassName="active" className="nav-links">
                    {languages[language].project}
                  </a>
                </li>
                <li className="nav-item">
                  <a href="" activeClassName="active" className="nav-links">
                    {languages[language].contact}
                  </a>
                </li>

                <div className="flags-desktop">
                  <li>
                    <img src={en} alt="en" onClick={handleIdiom("en")} />
                  </li>
                  <li>
                    <img src={br} alt="ptbr" onClick={handleIdiom("ptbr")} />
                  </li>
                  <li>
                    <img src={ru} alt="ru" />
                  </li>
                  <li>
                    <img src={jp} alt="jp" />
                  </li>
                </div>
              </ul>
            </div>

            <div className="flags">
              <img src={en} alt="en" />
              <img src={br} alt="ptbr" />
              <img src={ru} alt="ru" />
              <img src={jp} alt="jp" />
            </div>

            <div className="nav-icon" onClick={handleClick}>
              <i className={click ? "fa fa-times" : "fa fa-bars"}>
                <FaBars />
              </i>
            </div>
          </div>
        </nav>
      </div>
    </LanguagesProvider>
  );
}

export default Menu;

1 Answers1

1

We should wrap components where we want have access to language in Context.Provider. Then we can have access to context using hook useContext. Also added logic to save selected language to localStorage. Can be done like this:

import React, { useState, useContext } from "react";

// App.js

const defaultLanguage = "br";
export const LanguageContext = React.createContext();

function App() {
  const [language, setLanguage] = useState(() => {
    const langFromLocalStorage = window.localStorage.getItem("lang");

    return langFromLocalStorage ? langFromLocalStorage : defaultLanguage;
  });

  React.useEffect(() => {
    window.localStorage.setItem("lang", language);
  }, [language]);

  return (
    <div className="App">
      <LanguageContext.Provider value={[language, setLanguage]}>
        <Menu />
        <HomeComponent />
      </LanguageContext.Provider>
    </div>
  );
}

export default App;

// Menu.js :
// import { LanguageContext } from "...";

function Menu() {
  const languages = {
    en: {
      about: "about",
      project: "project",
      contact: "contact",
      slogan: "Think the Design, and I design the Code.",
      button: "Learn more",
    },
    br: {
      about: "sobre",
      project: "projetos",
      contact: "contatos",
      slogan: "Think the Design, and I design the Code.",
      button: "Learn more",
    },
    jp: {
      about: "nihon",
      project: "nihon",
      contact: "nihon",
      slogan: "nihon",
      button: "nihon",
    },
    ru: {
      about: "руский",
      project: "руский",
      contact: "руский",
      slogan: "руский",
      button: "руский",
    },
  };

  const [click, setClick] = useState(false);
  const Close = () => setClick(false);

  const [language, setLanguage] = useContext(LanguageContext);
  console.log(`language`, language);

  return (
    <>
      <div>
        <div
          className={click ? "main-container" : ""}
          onClick={() => Close()}
        />

        <nav className="navbar" onClick={(e) => e.stopPropagation()}>
          <div className="nav-container">
            <div className="links">
              <h1>Current Language is: {language}</h1>
              <ul className={click ? "nav-menu active" : "nav-menu"}>
                <li className="nav-item">
                  <a href="" activeClassName="active" className="nav-links">
                    {languages[language].about}
                  </a>
                </li>
                <li className="nav-item">
                  <a href="" activeClassName="active" className="nav-links">
                    {languages[language].project}
                  </a>
                </li>
                <li className="nav-item">
                  <a href="" activeClassName="active" className="nav-links">
                    {languages[language].contact}
                  </a>
                </li>

                <div className="flags-desktop">
                  <li>
                    <img
                      src={"en"}
                      alt="en"
                      onClick={() => setLanguage("en")}
                    />
                  </li>
                  <li>
                    <img
                      src={"br"}
                      alt="ptbr"
                      onClick={() => setLanguage("br")}
                    />
                  </li>
                  <li>
                    <img
                      src={"ru"}
                      alt="ru"
                      onClick={() => setLanguage("ru")}
                    />
                  </li>
                  <li>
                    <img
                      src={"jp"}
                      alt="jp"
                      onClick={() => setLanguage("jp")}
                    />
                  </li>
                </div>
              </ul>
            </div>
          </div>
        </nav>
      </div>
    </>
  );
}

const HomeComponent = () => {
  const [language] = useContext(LanguageContext);
  console.log(`language`, language);

  return (
    <div>
      <h1>HomeComponent</h1>
      <h1>Current Language is: {language}</h1>
    </div>
  );
};
Konstantin Modin
  • 1,313
  • 7
  • 12
  • Спасибо! One thing I understand from your comment. I was wrapping the wrong content. I'm trying to achieve something different or I didn't explain myself well. Example: In the Menu, once I change the language option, I'd like to also change the language in the Menu Component, I also would like the language in the Home component to be changed. – Flavio Andrade Jul 26 '21 at 11:50
  • That's why I removed the languages={...) from the "Menu" component and created a provider called "Languages". Because my initial goal is to share between all components. – Flavio Andrade Jul 26 '21 at 11:52
  • You can put your `` inside `` in `` and get current language there. I updated my answer. Added fake `` – Konstantin Modin Jul 26 '21 at 12:23
  • Sure you can have `dictionary` for all pages you need , then just import it where you need it – Konstantin Modin Jul 26 '21 at 12:37
  • I receive the following error: × TypeError: Object is not iterable (cannot read property Symbol(Symbol.iterator)) Because of the line const [language, setLanguage] = useContext(LanguageContext); – Flavio Andrade Jul 27 '21 at 13:46
  • This whole code in answer is working, just copy-paste it to 1 file. Then you can see what is different in your code. – Konstantin Modin Jul 27 '21 at 14:30
  • I really appreciate your help. The code indeed is working. I found the mistake, I was trying to use your example in a different way. – Flavio Andrade Jul 28 '21 at 03:42
  • Could you please share a link about the dictionary or doc? – Flavio Andrade Jul 28 '21 at 03:50
  • You can look my project with translations on GitHub: https://github.com/KonstantinModin/anastasia-site – Konstantin Modin Jul 28 '21 at 06:46