Create a React useTranslation hook to use everywhere-Part 2

//src/hooks/useTranslation.jsimport { useState, useEffect, useRef, useCallback } from 'react';const getLanguageFile = async({ language }) => {
const module = await import(`../languages/${language}/strings.json`);
return module.default
};
export default function useTranslation() {
const [{ language, strings }, setLanguage] = useState({
language: "en",
strings: {}
});
const isJsonFetched = useRef(false);const updateLanguage = useCallback(
async (newLanguage) => {
if (isJsonFetched.current && newLanguage === language) return;
const fetchedStrings = await getLanguageFile({ language: newLanguage });
isJsonFetched.current = true;
setLanguage({
language: newLanguage,
strings: fetchedStrings
});
}, [language]);
const t = (translation) => {
var keys = translation.split(".");
var result = strings;
for (let i = 0; i < keys.length; i++) {
result = result[keys[i]]
if (!result) break;
};
return result || translation
};
useEffect(() => {
updateLanguage(language)
}, [language, updateLanguage])
return { t, language, updateLanguage }
};
// src/pages/Home.jsximport useTranslation from "../hooks/useTranslation";export default function Home() {
const { t } = useTranslation();
return (
<div className="App">
<h3>{t("home.title.sub1")}</h3>
</div>
)};
  • Although translation part works smoothly now, there is one more issue we should address. Because the initial language state is set to “en” in our hook, the language of the whole application will do so in every page refresh. And this will lead to a poor user experience.
export default function setInitialLanguage() {const initialLang = localStorage.getItem("currentLang");if (!initialLang) {localStorage.setItem("currentLang", "en");
return "en";
} else {
return initialLang;
};
};
//src/hooks/useTranslation.jsimport { useState, useEffect, useRef, useCallback } from 'react';const getLanguageFile = async({ language }) => {
const module = await import(`../languages/${language}/strings.json`);
return module.default
};
const setInitialLanguage = () => {
const initialLang = localStorage.getItem("currentLang");
if (!initialLang) {
localStorage.setItem("currentLang", "en");
return "en";
} else {
return initialLang;
};
};
export default function useTranslation() {
const [{ language, strings }, setLanguage] = useState({
language: setInitialLanguage(),
strings: {}
});
const isJsonFetched = useRef(false);const updateLanguage = useCallback(
async (newLanguage) => {
if (isJsonFetched.current && newLanguage === language) return;
const fetchedStrings = await getLanguageFile({ language: newLanguage });
isJsonFetched.current = true;
setLanguage({
language: newLanguage,
strings: fetchedStrings
});
localStorage.setItem("currentLang", newLanguage);
}, [language]);
const t = (translation) => {
var keys = translation.split(".");
var result = strings;
for (let i = 0; i < keys.length; i++) {
result = result[keys[i]]
if (!result) break;
};
return result || translation
};
useEffect(() => {
updateLanguage(language)
}, [language, updateLanguage])
return { t, language, updateLanguage }
};
// might be also configured according to client ipconst setInitialLanguage = () => {
// declare allowed languages to prevent manipulation from local storage
var allowedLangs = ["tr", "en"];
// check localstorage for current lang
const initialLang = localStorage.getItem("currentLang");
if (!initialLang) {
// if not first;
// detect browser language
let browserLanguage = window.navigator.language || window.navigator.userLanguage;
// assign if there is a valid browser language
if (browserLanguage && allowedLangs.indexOf(browserLanguage) > -1) {
localStorage.setItem("currentLang", browserLanguage);
return browserLanguage;
};
// or assign a default language(en)
localStorage.setItem("currentLang", "en");
return "en";
} else {// set from localStorage if not malformed
if (allowedLangs.indexOf(initialLang) > -1) {
return initialLang;
} else {// if localStorage option is malformed
// set and return the defult language(en)
localStorage.setItem("currentLang", "en");
return "en";
}
};
};
  • Create a folder in the src directory called “utils” and move “getLanguageFile” and “setInitialLanguage” functions to this location. Do not forget to import them properly in our hook later.

Creating the Context Api

//src/context/LanguageContext.jsimport React, { createContext } from "react";
import useTranslation from "../hooks/useTranslation";
export const LanguageContext = createContext(null);export const LanguageProvider = ({ children }) => {
const { t, language, updateLanguage } = useTranslation();
return (
<LanguageContext.Provider value={{ language, updateLanguage, t }}>
{children}
</LanguageContext.Provider>
)
};
//src/App.jsimport './App.css';
import { LanguageProvider } from "./context/LanguageContext";
import Home from './pages/Home';
export default function App() {
return (
<LanguageProvider>
<Home />
</LanguageProvider>
)
};
//src/pages/Home.jsximport React, { useContext } from 'react';
import { LanguageContext } from "../context/LanguageContext";
import Navbar from '../components/Navbar';
export default function Home() {
const { t } = useContext(LanguageContext);
return (
<div className="App">
<Navbar />
<div className="App-header">
<h1>{t("home.title.main")}</h1>
<h3>{t("home.title.sub1")}</h3>
<h3>{t("home.title.sub2")}</h3>
</div>
</div>
)
};
//src/components/Navbar.jsximport React, { useContext } from 'react';
import { LanguageContext } from "../context/LanguageContext";
export default function Navbar() {
const { t, language, updateLanguage } = useContext(LanguageContext);
return (
<div
style={{ width: '100%', height: '5rem', backgroundColor: 'grey', display: 'flex', alignItems: 'center', justifyContent: 'space-around' }}>
<div>{t("navbar.about")}</div>
<div>{t("navbar.contact")}</div>
<select value={language} onChange={(e) => updateLanguage(e.target.value)}>
<option value="en">English</option>
<option value="tr">Turkish</option>
</select>
</div>
)
};

--

--

--

I'm an enthusiastic Web developer. I like learning new technologies and teaching them to other people.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Tealium Tools — Simple Cookie Setter

Interactive stories (beta)

node pg and postgres jsonb data type

Data fetching in React and Next.js with useSWR to impress your friends at parties

Manage Child to Parent State more Effectively with Hooks

https://twitter.com/runner_click/status/821460811809980419

Introducing npx: an npm package runner

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Durmuş

Durmuş

I'm an enthusiastic Web developer. I like learning new technologies and teaching them to other people.

More from Medium

Create a React useTranslation hook to use everywhere-Part 1

translation

Why We Choose React for frontend?

Make a react-window (2)

An investigative guide to React JS[DOM, Virtual DOM and JSX] Part-VI