Localization in React

#This Website#Projects#JavaScript#React#TypeScript

Localization in React

For this website, I wanted to provide its content not only in German but also in English. Since I also publish blogposts like this one here and will continue to do so, it should not be complicated to add or change translations and I would have to use them throughout the whole website, not just the blogposts.

react-localization

react-localization is a library that does exactly that. Here is a quick example on how it works:

//import the module import LocalizedStrings from 'react-localization'; //define translated texts let myTranslations = new LocalizedStrings({ en:{ title: "Localization in React", //add translations like this //jsx elements that you want to display can be added using lambda expressions: content: () => <> <p>Interesting text</p> </> }, de: { title: "Übersetzungen mit React", content: () => <> <p>Interessanter text</p> </> } }); function Post(){ //react component using function syntax //use translated texts like this: return <> <h1>{myTranslations.title}</h1> <p>{myTranslations.content()}</p> </> }

Switching Languages

By default, it uses the browsers language. To make switching languages possible for visitors of my website, I extended this system so that the current language can be specified in a query parameter. Visitors can change the current language by using a dropdown menu that changes this query parameter. Therefore, everyLocalizedStrings-Objects needs to use the language specified by the query-parameter.

Changing the language with react-localization is done for each LocalizedStrings-Object individually. So I extended the class LocalizedStrings:

import LocalizedStrings from 'react-localization'; export class MyLocalizedStrings extends LocalizedStrings { constructor(x) { return MyLocalizedStrings.create(x); } static create(x){ var ret = new LocalizedStrings(x); //set language in query param ret.setLanguage(getCurrentLanguage()); return ret; } } export function getCurrentLanguage(){ var quer = window.location.href.match(/[?&]lang=([^&]*)/); var navLanguage = getNavigatorLanguage(); return quer ? quer[1] : navLanguage; } export function getNavigatorLanguage() { var navLanguage = navigator.language; if (navLanguage.includes("-")) { navLanguage = navLanguage.split("-")[0]; } return navLanguage; }

This worked well. But when clicking links that lead to other pages on my website, the lang parameter disappeared. Query Parameters are not propagated. Therefore, I created a React Component to use as a Link that would redirect the visitor to the next page while keeping the lang parameter:

import React from 'react'; import { useSearchParams } from 'react-router-dom'; export function NavLinkLang(props) { const [searchParams, setSearchParams] = useSearchParams(props.to); const language = searchParams.get('lang'); const appendSymbol = (props.to.includes && (/(?)(?=(?:[^'"]|'[^']*'|"[^"]*")*$)/).test(props.to)) ? "&" : "?"; const appendix = language ? appendSymbol + "lang=" + language : ""; const href = window.origin + "/#" + props.to + appendix; //instead of using "Link" or "<a>", we use a div and implement link functionality ourselves so we don't have nested links (not html compliant) return <> <div {...props} style={{ cursor: 'pointer' }} className='navlinklang' onClick={(e) => { e.preventDefault(); e.stopPropagation(); //TODO extract to router.tsx: window.location = href; }} /> <a href={href} style={{ display: "none" }}>Link</a> </>; }

This is the Component for toggling the language:

import React, { useEffect, useRef, useState } from 'react'; import { Link, useSearchParams } from 'react-router-dom'; import { Button, Dropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'react-bootstrap'; import { NavLink } from 'react-router-dom'; import { MyLocalizedStrings, getNavigatorLanguage } from './MyLocalizedStrings'; //import { strings } from '..'; export const strings = new MyLocalizedStrings({ en: { language: "Language", flag: "🇬🇧", }, de: { language: "Sprache", flag: "🇩🇪", }, }) export function LanguageToggle() { const [searchParams, setSearchParams] = useSearchParams(); const myParam = searchParams.get('lang'); useEffect(() => { if (myParam && myParam!=strings.getLanguage()) { window.location.reload(); } }, [myParam]); function setLanguage(newLang) { searchParams.set("lang", newLang) //if language is the same as the navigator language, remove the language parameter if(newLang==getNavigatorLanguage()){ searchParams.delete("lang"); setSearchParams(searchParams); window.location.reload(); } setSearchParams(searchParams); } return <Dropdown> <DropdownToggle>{strings.flag} {strings.language}</DropdownToggle> <DropdownMenu> <DropdownItem onClick={() => { setLanguage("en"); }}>English</DropdownItem> <DropdownItem onClick={() => { setLanguage("de"); }}>Deutsch</DropdownItem> </DropdownMenu> </Dropdown>; }

Comments

Feel free to leave your opinion or questions in the comment section below.