Originally posted on tech.gadventures.com
Imagine you’re in a Roman train station in the middle of the night, and nobody’s around. You need to purchase a ticket to Venice. The ticketing software is in Italian. You don’t speak Italian. After a bit of a struggle, you manage to get a ticket. You get on the train. More Italian. A 3 hour trip. You look at the welcome sign. ‘Welcome to Florence’. Sigh. Florence is not Venice.
In the ideal world, we’d all speak many languages, like some of our European travelers. But not everyone understands English, so it’s better to support as many languages as possible.
As some of you may know, G has a travel app called Good to Go. There are certain requirements you may need to complete before you travel. These include sending us a copy of your visa, your jacket size, your food preference, etc – we refer to these as “trip requirements”. Before travelers start their adventure, Good to Go helps them make sure they have all the things they need before they travel.
Thousands of travelers use it to complete their trip requirements before they begin. Every single one of our departures goes through Good to Go. It’s a big star. In fact, G-Stock 23 marks the 1 year anniversary of Good to Go. We continue to work on it and make it better every single day.
Last month, we brought German support to Good to Go, which is written in React. It was pretty easy thanks to Yahoo’s React-Intl library. This post details my experience adding internationalization to Good to Go.
Firstly, when it comes to translating, the order of the words is important, as well as the formatting of dates and names. Grouped together, these things are known as “locale data”.
Installing react-intl is simple enough, but there are a few additional steps you need to complete after the initial yarn add react-intl. Let’s go over them now.
Babel
I’m going to illustrate how to do this with Babel and Webpack. First, install the babel-plugin-react-intl plugin (yarn add babel-plugin-react-intl). It will extract the strings marked for translation. To activate the plugin, you have to then add its name to your .babelrc file.
"plugins": [
...,
["react-intl", {
"messagesDir": "./src/translations",
"enforceDescriptions": false
}],
...
]
In the example above, react-intl will extract tagged messages into src/translations. The messages are stored in JSON files, inside directories that are named after your components.
When it’s time to send the messages to a translation service, you will probably want all of them in a single file. For this reason, the creators of the library have written a script that you can use to combine all the strings into a single file. To see a more detailed example, see this post.
Locale Data
The locale data for each language is provided and imported independently so you’re only sending down what you need. In the following example, we’re importing the locale data for English and German.
import { addLocaleData } from 'react-intl'
import enLocaleData from 'react-intl/locale-data/en'
import deLocaleData from 'react-intl/locale-data/de'
So the process is:
- Install the library
- Install the babel plugin
- Import the locale data
Finally, you wrap your root component with IntlProvider, so that all the translated messages and locale data can be loaded into the React Intl Components used by your app.
<IntlProvider locale="de" messages={messages}>
<Root />
</IntlProvider>
Another method is to inject these values right into the intl reducer during the “boot up part of your app’s lifecycle, like index.js or a configureStore method.
{
...,
intl: {
defaultLocale: 'en',
locale: 'en',
messages: allMessages.en
},
...
}
Formatting with React-Intl
The heart of react-intl is its tagging system. You can tag (or mark) strings for translation by wrapping them in a Rect component or using the API.
- A React Component
import { FormattedMessage } from 'react-intl'
<FormattedMessage
id="header.selectPax"
defaultMessage="Select the traveller for whom you'd like to enter requirement information."
/>
Using the component is straight forward, and powerful. For example, they can take values, illustrated in this example:
<FormattedMessage
id="form.name"
defaultMessage="{name}"
values={{ name }}
/>
The object you provide to values can be another FormattedMessage, meaning you can string them along like a chain to create a message with HTML tags!
<FormattedMessage
id="body.text"
defaultMessage="{bodyText}"
values={{
bodyText:
<div>
<h2>
<FormattedMessage
id="bodyText.1"
defaultMessage="Ready for a G Adventure?" />
</h2>
</div>
}}
/>
- API
import { defineMessages } from 'react-intl'
const formButtons = defineMessages({
login: {
id: 'formButtons.login',
defaultMessage: 'Login'
}
})
With the API, you define your message ahead of time in a simple Javascript object.
The API provides you with translated strings in places where a React component would not be appropriate. For example, inside of a <button>.
import { injectIntl } from 'react-intl'
const myForm = ({ intl }) => {
<button
type="submit"
onClick={handleSubmit}>
{intl.formatMessage(formButtons.login)}
</button>
}
export default injectIntl(Waiver)
You’ve seen two ways you can use the react-intl library to translate your strings. I recommend using it everywhere. You will delight your users when they see your content in their native language.
Look out for Part 2 of this series on translation, where we go over how to build out a Language Picker that uses react-intl.
Keep following us on Medium and Twitter for more snippets of the Tech world according to G!
Want to work at G Adventures? Join our growing team and travel the world! Check out all our jobs and apply today.
Top comments (1)
Thanks, But in case you need to translate a string value that returns another string value not a react component, would be much helpful if you mentioned that in the post.