What ways to change language in React can you suggest without using external libraries? My way is to use the ternary operator {language === 'en'? 'title': 'titre'}
. If language is en
, displaytitle
if not, display titre
. What other way can you remend. For example, that the translations should be placed in a separate json file.
Code here:
class App extends Component {
constructor() {
super();
this.state = {
language: 'en'
};
}
changeLanguage = () => {
this.setState({
language: 'fr'
})
}
render() {
const {language} = this.state;
return (
<div>
<p>
{language === 'en' ? 'title' : 'titre'}
</p>
<button onClick={this.changeLanguage}>change language</button>
</div>
);
}
}
What ways to change language in React can you suggest without using external libraries? My way is to use the ternary operator {language === 'en'? 'title': 'titre'}
. If language is en
, displaytitle
if not, display titre
. What other way can you remend. For example, that the translations should be placed in a separate json file.
Code here: https://stackblitz./edit/react-eu9myn
class App extends Component {
constructor() {
super();
this.state = {
language: 'en'
};
}
changeLanguage = () => {
this.setState({
language: 'fr'
})
}
render() {
const {language} = this.state;
return (
<div>
<p>
{language === 'en' ? 'title' : 'titre'}
</p>
<button onClick={this.changeLanguage}>change language</button>
</div>
);
}
}
Share
Improve this question
edited Aug 17, 2019 at 20:05
Touffy
6,56125 silver badges29 bronze badges
asked Aug 17, 2019 at 18:52
UmbroUmbro
2,20412 gold badges46 silver badges106 bronze badges
3
- 1 What do you mean by "change the language" ? your example shows a state change, but are you actually trying to do i18n (i.e. use the language state to render localized strings) with ternary expressions ? – Touffy Commented Aug 17, 2019 at 18:59
-
@Touffy
change langauge
-->switch language
. – Umbro Commented Aug 17, 2019 at 19:06 -
@Touffy I detect language setting in browser;
navigator.languages && navigator.languages.length) ? navigator.languages[0] : navigator.userLanguage || navigator.language || navigator.browserLanguage || 'en';
– Umbro Commented Aug 17, 2019 at 19:09
2 Answers
Reset to default 8Internationalization (i18n) is a hard problem with a few existing, standard solutions designed by expert translators and linguists to account for the breadth of language quirks across the world. You shouldn't generally try to e up with your own solution, even when you are fluent in all target languages.
That doesn't mean you need a library (you could implement one of those standards yourself) but writing the i18n logic inline will not scale and probably won't work well.
The easiest case of i18n is if you're translating strings that do not depend on context and are plete sentences with no interpolation. You could get away with a very basic approach there, like using a big dictionary of translations and just looking up each string in it. It would look sort of like your ternary but at least it would scale for many languages, and it would be reasonable to do that with no library:
l10n = {
'title': {en: 'title', fr: 'titre'}
}
<p>
{l10n['title'][lang]}
</p>
However, if there is going to be string interpolation in your website/application/whatever, please consider a library that implements, say, ICU.
Now, let me show you why it would be a bad idea. Suppose you have the string "you can see (n) rocks" where you want to replace (n) with an actual number, and you want the sentence to be grammatically correct so you need to pute number agreement, right ? so, "0 rocks", "1 rock", "2+ rocks"… looks like English plural is just adding an "s" (not true, but let's assume for now), you could implement that with ternaries. I see you used French in your example so, how about that ? "0 cailloux", "1 caillou", "2+ cailloux". Right, there are multiple plural forms in French. How do you write your code to account for that ? And what if you need a German translation ? maybe the translator will decide that the object should go first in the sentence, rather than last. How does your code handle word order based on language ?
All these problems should be delegated to the translator who encodes them into an ICU string, which is then evaluated by some code given a context to get a correct translation. Whether you use a library or implement it yourself, what you want in the end is some function — let's call it localize(string, context)
that is pretty much independent from React and that you use in your ponents like this:
import localize from './somewhere'
<p>
{localize('title')}
</p>
If you really want to, you can pass the locale as an argument and have it stored in React's state somehow. This library decided it wasn't necessary because real users rarely switch language and it's OK to reload the whole application when that happens.
I just implemented a simple language ponent for work that uses a Localisation context/provider and a dictionary (e.g JSON). I'll go through the steps, and there's a workable codesandbox example at the end. This is a very basic approach, but it works well for us at the moment.
The example has:
1) A simple "dictionary" that contains the tokens you want to translate in each language defined by a short code
{ EN: { wele: 'Wele' }, FR: { wele: 'Bienvenue' }, IT: { wele: 'Benvenuto' } };
2) An initial state and reducer that you can update when the language changes
export const initialState = {
defaultLanguage: 'EN',
selectedLanguage: 'IT'
}
export function reducer(state, action) {
const { type, payload } = action;
switch (type) {
case 'LANGUAGE_UPDATE': {
return { ...state, selectedLanguage: payload };
}
default: return state;
}
}
3) A Localisation Context/Provider. You can wrap your code in the provider and every child ponent can get access to the state through the context. We import the dictionary and state/reducer, create the new context and then set up the provider into which we pass the state and dictionary.
import dictionary from './dictionary';
import { initialState, reducer } from './localisationReducer';
export const LocalisationContext = React.createContext();
export function LocalisationProvider({ children }) {
const localisationStore = useReducer(reducer, initialState);
return (
<LocalisationContext.Provider value={{ localisationStore, dictionary }}>
{children}
</LocalisationContext.Provider>
);
}
4) An example app. You can see the LocalisationProvider wrapping the other elements, but also a dropdown, and a ponent called Translate. I'll describe those next.
<LocalisationProvider>
<Dropdown />
<div className="App">
<h1>
<Translate token="wele" />
</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
</LocalisationProvider>
5) The dropdown accesses the Localisation context and builds a dropdown with the languages. The key part is the handleSelected
function which uses the dispatch
from the localisation store to change the state (update the language):
import { LocalisationContext } from './localisation';
const langs = [
{ shortCode: 'EN', label: 'English' },
{ shortCode: 'FR', label: 'Français' },
{ shortCode: 'IT', label: 'Italiano' }
];
export function Dropdown() {
const {
localisationStore: [ state, dispatch ]
} = useContext(LocalisationContext);
const { selectedLanguage } = state;
const handleSelected = (e) => {
const { target: { value } } = e;
dispatch({ type: 'LANGUAGE_UPDATE', payload: value });
}
function getOptions(langs, selectedLanguage) {
return langs.map(({ shortCode, label }) => {
return <option value={shortCode}>{label}</option>
});
}
return (
<select onChange={handleSelected}>
{getOptions(langs, selectedLanguage)}
</select>
);
}
6) The Translate ponent which also accesses the state and dictionary through the context, and performs the translation based on the selected language.
import { LocalisationContext } from './localisation';
export default function Translator({ token }) {
const {
localisationStore: [state], dictionary
} = useContext(LocalisationContext);
const {
selectedLanguage, defaultLanguage
} = state;
const translatedToken = dictionary[selectedLanguage][token] || dictionary[defaultLanguage][token];
return (
<Fragment>
{translatedToken}
</Fragment>
);
}
Here's the codesandbox example for you to explore. Just select a new language from the dropdown to see the main "wele" text change.