Creating small, maintainable apps with React is no problem at all - unless you're completely new to front-end development. Nowadays, it's also fairly easy to scale your React app with Internationalization (I18n). I want to show you how uncomplicated it is to set up a new or existing project to manage I18n.
2023 disclaimer: the title of this article is misleading. i18n
is complex and should be planned carefully before being implemented. Although, if you're looking to play around with it or set it up, this is still relevant.
I'll be using the react-intl
library for this article. react-intl
is a library that provides re-usable components and an API to format text in different languages.
Initial setup
In this case, we'll create a simple React app that lets the user switch between languages at any time. Since it's only for languages, this doesn't encompass the whole i18n scope, but it's a good start.
Lets get started with a project set up using (create-react-app).
Create the project:
$ create-react-app intl-test
Within your project, run:
$ npm i react-intl
react-intl setup
After that, we want to wrap our root component with the IntlProvider
component, who's job is feeding our language across components.
// ...
import { IntlProvider } from 'react-intl'
class App extends Component {
render() {
return (
<IntlProvider
messages={{ /* Our texts will go here */ }}
locale="en"
>
<div className="App">
<h1>Hello World!</h1>
</div>
</IntlProvider>
)
}
}
With locale
and messages
in IntlProvider
, you can access and modify their value dynamically. We want to play around with the messages
prop. This prop receives an object with your defined texts in different languages. These texts are accessed via an id
which can be arbitrarily declared.
For example, let's look at a greeting string, "Hello World!". We can give it an id
of home.greeting
. Lets create a messages.js
file in the src
directory:
// All the app's messages would go hereexport default {
en: {
'home.greeting': 'Hello World!',
},
es: {
'home.greeting': 'Hola Mundo!',
},
}
This object will be what the messages
prop receives in the IntlProvider
.
Now, we replace our usually hard-coded string with FormattedMessage
which injects the value of the given id
.
Lets create a new component Home.js
that holds our greeting:
// ...
import { FormattedMessage } from "react-intl";
const Home = () => (
<div>
<h1>
<FormattedMessage id="home.greeting" defaultMessage="Hello World!" />
</h1>
</div>
);
Our root App
component should now resemble this:
// ...
import Home from "./Home";
import messages from "./messages";
class App extends Component {
render() {
return (
<IntlProvider messages={messages["en"]} locale="en">
<div className="App">
<Home />
</div>
</IntlProvider>
);
}
}
Now we need a way to change languages.
Adding state to the App
component can help determine which language is to be displayed.
class App extends Component {
state = {
lang: 'en' // default,
locale: 'en'
}
render() {
return (
<IntlProvider
messages={messages[this.state.lang]}
locale={this.state.locale}
>
...
</IntlProvider>
)
}
}
Notice how on the messages
prop, the 'en' property is no longer hard-coded, but dynamically referenced from our state now. So lang
can be changed to 'es' or any other defined language.
To change languages via the UI, one option could be adding some buttons that modify this.state.lang
on click.
class App extends Component {
state = {
lang: 'en' // default,
locale: 'en'
}
handleChangeLanguage = lang => this.setState({ lang })
render() {
return (
<IntlProvider
messages={messages[this.state.lang]}
locale={this.state.locale}
>
<div className="App">
<header>
<button onClick={() => this.handleChangeLanguage('en')}>
EN
</button>
<button onClick={() => this.handleChangeLanguage('es')}>
ES
</button>
</header>
<Home />
</div>
</IntlProvider>
)
}
}
No need to pass this.state.lang
to other child components as props, react-intl
helps out with that!
Of course, no app is THAT simple, but for the sake of practicality, I decided to keep it this minimal. You can add more components with FormattedMessage
s and every message will transition.
Furthermore, react-intl
provides some extra functionality for all kinds of I18n magic like Redux integration (React), format helpers, personalized formatting for dates, currencies, and much more. Check out the docs.
There are many I18n solutions for React and Javascript that might try to solve the same problem. However, there aren't many solutions as straightforward, friendly 🤗 and scalable as react-intl
.