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 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>
</>
}
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>;
}
Feel free to leave your opinion or questions in the comment section below.