React Js always keep lang parameter in url even after moving to other page

355 Views Asked by At

I am creating a multilingual app in react js and I want to always have the ?lang=languageName parameter in url. I am using react-i18next for translation. Below code which works fine if I refresh the page:

import React, { useEffect } from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';

import NavBar from './layout/navbar';
import Home from './pages/home';
import { useTranslation } from 'react-i18next';
import SignUp from './pages/auth/signup';

function App() {
  const { i18n } = useTranslation();
  const queryParam = new URLSearchParams(window.location.search);
  const lang = queryParam.get('lang');

  useEffect(() => {
    if (!lang) {
      queryParam.set('lang', i18n.language);
      window.history.replaceState({}, '', `?${queryParam}`);
    }
  });

  return (
    <>
      <BrowserRouter>
        <>
          <NavBar />
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="home" element={<Home />} />
            <Route path="signup" element={<signup />} />
          </Routes>
        </>
      </BrowserRouter>
    </>
  );
}

export default App;

It works fine when I reload page. But when I click on signup button and the url changes to http://example.com/signup it doesn't add lang parameter with it. It should be like http://example.com/signup?lang=languageName.

2

There are 2 best solutions below

2
feyzullahyildiz On

Your useEffect does not have empty array for dependencies. It should be like this

OLD Answer, that is not working

 useEffect(() => {
    if (!lang) {
      queryParam.set('lang', i18n.language);
      window.history.replaceState({}, '', `?${queryParam}`);
    }
  }, []); // <--- THIS IS IMPORTANT. Otherwise, it will executed for every render

UPDATED

 const { i18n } = useTranslation();
 useEffect(() => {
    const lang = queryParam || 'en';
    i18n.changeLanguage(lang );
    
 }, []);

My Recommendation

  • prepare 2 files
    • i18n/config.js
    • utils/language-service.js
// utils/language-service.js

const SUPPORTED_LANGUAGES = [
    'en-US',    // FIRST INDEX, DEFAULT LANGUAGE
    'en-GB',
    'es',
    'fr',
];
const LOCALSTORAGE_KEY = "i18nextLng";

const getDefault = () => {
    return SUPPORTED_LANGUAGES[0];
}
const setCurrentLanguage = (langKey) => {
    const exists = SUPPORTED_LANGUAGES.includes(langKey);
    if (!exists) {
        console.error('LANGUAGE DOES NOT EXISTS in SUPPORTED_LANGUAGES');
        console.error('HERE is the list', SUPPORTED_LANGUAGES);
        console.error('DEFAULT LANGUAGE WILL BE USED');
        localStorage.setItem(LOCALSTORAGE_KEY, getDefault());
        return;
    }
    localStorage.setItem(LOCALSTORAGE_KEY, langKey);
}
const getCurrentLanguage = () => {
    /*
      // Write your language detection from URL.
      const query = new URLSearchParams(window.location.search);
      const langKey = query.get('lang');
    */
    // const langKey = localStorage.getItem(LOCALSTORAGE_KEY);
    const exists = SUPPORTED_LANGUAGES.includes(langKey);
    if (!exists) {
        const defaultKey = getDefault();
        setCurrentLanguage(defaultKey);
        return defaultKey;
    }
    return langKey;
}


export const LanguageService = {
    getDefault,
    getCurrentLanguage,
    setCurrentLanguage,
}
// i18n/config.js
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import * as Lang from "./lang";
import { LanguageService } from "../utils";

i18n
  .use(initReactI18next)
  .init({
    debug: false,
    lng: LanguageService.getCurrentLanguage(),
    fallbackLng: LanguageService.getDefault(),
    resources: {
      "en-US": {
        translation: Lang.enUS,
      },
      "en-GB": {
        translation: Lang.enGB,
      },
      es: {
        translation: Lang.es,
      },
      fr: {
        translation: Lang.fr,
      },
    },
  });
i18n.on("languageChanged", () => {
  LanguageService.setCurrentLanguage(i18n.language);
});


2
Drew Reese On

I suggest using the useSearchParams hook to update the queryString.

import {
  Routes,
  Route,
  useSearchParams,
} from 'react-router-dom';

function App() {
  const { i18n } = useTranslation();
  const [searchParams, setSearchParams] = useSearchParams();

  const lang = searchParams.get('lang');

  useEffect(() => {
    if (!lang) {
      searchParams.set('lang', i18n.language);
      setSearchParams(searchParams, { replace: true });
    }
  }, [lang]);

  return (
    <>
      <NavBar />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="home" element={<Home />} />
        <Route path="signup" element={<signup />} />
      </Routes>
    </>
  );
}
import { BrowserRouter } from 'react-router-dom';

...

<BrowserRouter>
  <App />
</BrowserRouter>