Curated List: Our Best of Libraries for React I18n – Phrase
source link: https://phrase.com/blog/posts/react-i18n-best-libraries/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
The Best Libraries for React I18n
Let's not beat around the bush: React is a strong JavaScript library that lets you build scalable UI interfaces. When it comes to internationalizing your app, though, it does not offer a built-in solution. Fortunately, there are some amazing open-source libraries that can help you manage your i18n project successfully from start to finish.
- The Phrase Blog
- Developers
- The Best Libraries for React I18n
This curated list features the best libraries for React i18n. It will walk you through the pros and cons of each option in terms of flexibility, scalability, and most of all, developer productivity.
Since almost every programming language has different rules and conventions, and adapting to them within the scope of those libraries may be tricky, understanding the pros and cons of each library might take time and effort.
At the time of writing, we are using the latest versions of React v16.11.0 and React Router v5.1.2. You can find all code examples in our GitHub repo.
Our base project
We will start off by layering a base project that we would like to localize. It would be a typical, unopinionated React project with a minimal boilerplate, useful for starting out fresh. Create a new app using Create React App and update the App.js with the following content:
import React from 'react'; import {Switch, Route, HashRouter as Router} from 'react-router-dom'; import Home from "./Home"; import './App.css'; const App = () => ( <Router> <Switch> <Route exact path="/" component={Home} /> </Switch> </Router> ); export default App;
import React, {Component} from 'react'; import logo from './logo.svg' import './Home.css'; class Home extends Component { render() { return ( <div className="Home"> <div className="Home-header"> <img src={logo} className="Home-logo" alt="logo"/> <h2>Welcome to React.js</h2> </div> <div className="Home-container"> <div className="Home-items"> <div className="Home-item"> <h3 className="focus">Declarative</h3> <div><p>React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.</p> <p>Declarative views make your code more predictable and easier to debug.</p> </div> </div> <div className="Home-item"> <h3 className="focus">Component-Based</h3> <div> <p>Build encapsulated components that manage their own state, then compose them to make complex UIs.</p> <p>Since component logic is written in JavaScript instead of templates, you can easily pass rich data through your app and keep state out of the DOM.</p> </div> </div> </div> </div> </div> ); } } export default Home;
And the Home.css component as:
.Home { text-align: center; } .Home-logo { height: 40vmin; } .Home-header { background-color: #282c34; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); color: white; } .Home-link { color: #09d3ac; }
React-intl
This library is one of the most popular solutions for i18n in React and it’s now part of Formatjs. Offered by Yahoo is bundled with common locale data like dates, currencies, numbers, and support for over 150+ languages. It builds upon the concepts of the ECMAScript’s ECMAScript’s Internationalization API namespace.
In order to use it, we need to configure a few things first.
First we need to install it:
yarn add react-intl
Then on the root of the application just before we render the initial component, we need to add the following bootstrapping code:
/* react-intl imports */ import { IntlProvider } from 'react-intl'; /* Define your translations */ let i18nConfig = { locale: 'el', messages: { "home.welcome": "Καλώς 'Ηρθατε στο {name}!", "home.declarative": "Δηλωτικό" } };
Notice that the i18nConfig contains all the specific language translations for the current locale. We could have loaded this config before the application starts using a JSON Webpack loader or an Ajax call.
When we have loaded our supported locale and config messages, then we only need to wrap the application with the that will apply the i18n context available for our internal React Components:
ReactDOM.render( <IntlProvider locale={i18nConfig.locale} defaultLocale={i18nConfig.locale} messages={i18nConfig.messages} > <App /> </IntlProvider>, document.getElementById('root'));
Finally, we are ready to provide our translated components. We need to convert our existing text in the Home component into a series of FormattedMessage components:
import React, {Component} from 'react'; import {FormattedMessage} from 'react-intl'; import logo from './logo.svg' import './Home.css'; class Home extends Component { render() { return ( <div className="Home"> <div className="Home-header"> <img src={logo} className="Home-logo" alt="logo"/> <h2><FormattedMessage id="home.welcome" values={{name: 'React.js'}}/></h2> </div> <div className="Home-container"> <div className="Home-items"> <div className="Home-item"> <h3 className="focus"> <FormattedMessage id="home.declarative"/></h3> <div><p><FormattedMessage id="home.declarative.p1" values={{name: <i>React</i>}}/></p> <p><FormattedMessage id="home.declarative.p2"/></p> </div> </div> <div className="Home-item"> <h3 className="focus"><FormattedMessage id="home.component-based"/></h3> <div> <p><FormattedMessage id="home.component-based.p1"/></p> <p><FormattedMessage id="home.component-based.p2"/></p> </div> </div> </div> </div> </div> ); } } export default Home;
Then, we need to update our translations to match the keys for each one of the messages:
let i18nConfig = { locale: 'el', messages: { "home.welcome": "Καλώς 'Ηρθατε στο {name}!", "home.declarative": "Δηλωτικό", "home.declarative.p1": "To {name} καθιστά ανώφελη τη δημιουργία διαδραστικών διεπαφών χρήστη. Σχεδιάστε απλές προβολές για κάθε κράτος στο δικό σας\n" + " εφαρμογή και το React θα ενημερώσει αποτελεσματικά και θα αποδώσει τα σωστά στοιχεία όταν τα δεδομένα σας\n" + " αλλαγές.", "home.declarative.p2": "Οι δηλωτικές προβολές καθιστούν τον κώδικα πιο προβλέψιμο και πιο εύκολο στον εντοπισμό σφαλμάτων.", "home.component-based": "Βασισμένο σε στοιχεία", "home.component-based.p1": "Δημιουργήστε ενσωματωμένα στοιχεία που διαχειρίζονται τη δική τους κατάσταση, και στη συνέχεια συνθέστε τα για να δημιουργήσετε σύνθετα UI.", "home.component-based.p2": "Δεδομένου ότι η λογική συνιστωσών είναι γραμμένη σε JavaScript αντί για πρότυπα, μπορείτε εύκολα να περάσετε πλούσια δεδομένα\n" + " μέσω της εφαρμογής σας και να κρατήσετε την κατάσταση εκτός του & nbsp; DOM.", } };
Now, if we notice the app will load with the Greek translations. Notice also that the FormattedMessage can return also React components and not just text.
Among other things, React-intl offers some extra components:
- FormattedDate: Formats locale-specific dates.
- ForrmattedTime: Formats locale-specific times.
- FormattedRelative: Formats locale-specific relative durations.
- FormattedNumber: Formats locale-specific numbers and decimals.
- FormattedPlural: Formats locale-specific plurals and quantities.
There is also support for React Hooks via the useIntl hook. To use it in our Functional Components, instead of importing the above components, we just call the hook and it will return the imperative formatting API as an object for us:
const HomeHeader = () => { const intl = useIntl(); return ( <div className="Home-header"> <img src={logo} className="Home-logo" alt="logo"/> <h2>{intl.formatMessage({id: 'home.welcome'}, {name: 'React.js'})}</h2> </div> ) }; class Home extends Component { render() { return ( <div className="Home"> <HomeHeader /> ...
React-intl offers a good overall package for internationalizing your React Application. However, it is not without its disadvantages. You cannot use it for non-react components as it requires the top level component to inject the context to the children. We will see how the next library helps with providing a holistic solution to this problem.
Learn how to find the best i18n manager and follow our best practices for making your business a global success.
Check out the guideReact-intl-universal
react-intl-universal is a React internationalization package developed by Alibaba Group. It builds on top of React-intl by allowing non-React components use the library by providing a singleton object that can be used to load the current locale. Let’s see how we can use this library in our project.
Install it first:
yarn add react-intl-universal
Using the i18nConfig we need to load the translations and pass them through the intl.init method call:
import React, {Component} from "react"; import intl from 'react-intl-universal'; // common locale data require('intl/locale-data/jsonp/el.js'); export default class App extends Component { state = {initDone: false}; componentDidMount() { this.loadLocales(); } loadLocales() { intl.init({ currentLocale: i18nConfig.locale, locales, }) .then(() => { // After loading CLDR locale data, start to render this.setState({initDone: true}); }); } ...
Once we update the state we can render the App component:
render() { return ( this.state.initDone && <Router> <Switch> <Route exact path="/" component={Home} /> </Switch> </Router> );
import intl from 'react-intl-universal'; intl.get('home.declarative'); // Simple text message intl.getHtml('home.declarative.p1') // HTML message intl.get('not-exist-key').defaultMessage('Μύνημα που δεν υπάρχει') // Default message intl.get('home.welcome', {name:'React.js'}) // Message with variables.
Now the API for accessing those translations is simple:
import intl from 'react-intl-universal'; intl.get('home.declarative'); // Simple text message intl.getHtml('home.declarative.p1') // HTML message intl.get('not-exist-key').defaultMessage('Μύνημα που δεν υπάρχει') // Default message intl.get('home.welcome', {name:'React.js'}) // Message with variables.
It can also handle locale-specific messages for Times, Dates, Decimals and Plurals.
LinguiJS
LinguiJS is considered a newcomer in this list, offering an impressive array of powerful features for i18n. It’s clean, simple and with low overhead. Not to mention it works with React too!
Let’s see an example usage. First we need to install a bunch of libraries:
yarn add --dev @lingui/cli @lingui/macro @babel/core babel-core@bridge yarn add @lingui/react
Next we need to add a configuration file for the CLI tool that will extract and compile the messages. Create the following file with this config:
.jslingui
{ "localeDir": "src/locales/", "srcPathDirs": ["src/"], "format": "minimal", "fallbackLocale": "el", "sourceLocale": "en" }
We specify that we need a JSON format (using minimal) for our message formats and they will be saved in the src/locales folder.
Now let’s create the Home.js Component:
import React from 'react'; import {Trans} from "@lingui/macro" import logo from './logo.svg' import './Home.css'; const Home = ({name}) => { return ( <div className="Home"> <div className="Home-header"> <img src={logo} className="Home-logo" alt="logo"/> <Trans render="h2" id="home.welcome">Welcome to {name}!</Trans> </div> <div className="Home-container"> <div className="Home-items"> <div className="Home-item"> <h3 className="focus"> <Trans id="home.declarative">Declarative</Trans> </h3> <div> <p> <Trans id="home.declarative.p1"> {name} makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes. </Trans> </p> <p> <Trans id="home.declarative.p2"> Declarative views make your code more predictable and easier to debug. </Trans> </p> </div> </div> <div className="Home-item"> <h3 className="focus"> <Trans id="home.component-based">Component-Based</Trans> </h3> <div> <Trans render="p" id="home.component-based.p1"> Build encapsulated components that manage their own state, then compose them to make complex UIs. </Trans> <Trans render="p" id="home.component-based.p2"> Since component logic is written in JavaScript instead of templates, you can easily pass rich data through your app and keep state out of the DOM. </Trans> </div> </div> </div> </div> </div> ); }; export default Home;
Then we need to extract and compile the messages that we are going to translate:
$ npm run extract > [email protected] extract /Users/itspare/WebstormProjects/react-i18n-libraries > lingui extract Catalog statistics: ┌─────────────┬─────────────┬─────────┐ │ Language │ Total count │ Missing │ ├─────────────┼─────────────┼─────────┤ │ el │ 7 │ 7 │ │ en (source) │ 7 │ - │ └─────────────┴─────────────┴─────────┘ (use "npm run add-locale <locale>" to add more locales) (use "npm run extract" to update catalogs with new messages) (use "npm run compile" to compile catalogs for production) $ npm run compile > lingui compile Compiling message catalogs… Done!
Note that, when you run the app the first time you will see only the English translations and not the Greek. That’s because the Greek translations are missing. We will have to add them to the src/locales/el/messages.json before we run the compile command:
{ "home.component-based": "Βασισμένο σε στοιχεία", "home.component-based.p1": "Δημιουργήστε ενσωματωμένα στοιχεία που διαχειρίζονται τη δική τους κατάσταση, και στη συνέχεια συνθέστε τα για να δημιουργήσετε σύνθετα UI.", "home.component-based.p2": "Δεδομένου ότι η λογική συνιστωσών είναι γραμμένη σε JavaScript αντί για πρότυπα, μπορείτε εύκολα να περάσετε πλούσια δεδομένα μέσω της εφαρμογής σας και να κρατήσετε την κατάσταση εκτός του & nbsp; DOM.", "home.declarative": "Δηλωτικό", "home.declarative.p1": "To {name} καθιστά ανώφελη τη δημιουργία διαδραστικών διεπαφών χρήστη. Σχεδιάστε απλές προβολές για κάθε κράτος στο δικό σας εφαρμογή και το React θα ενημερώσει αποτελεσματικά και θα αποδώσει τα σωστά στοιχεία όταν τα δεδομένα σας αλλαγές.", "home.declarative.p2": "Οι δηλωτικές προβολές καθιστούν τον κώδικα πιο προβλέψιμο και πιο εύκολο στον εντοπισμό σφαλμάτων.", "home.welcome": "Καλώς 'Ηρθατε στο {name}!" }
Once we’ve completed those step’s, we only have to load those messages into the I18nProvider component:
import { I18nProvider } from '@lingui/react'; import catalogEl from './locales/el/messages.js'; import catalogEn from './locales/en/messages.js'; const catalogs = { en: catalogEn, el: catalogEl }; ReactDOM.render( <I18nProvider language={i18nConfig.locale} catalogs={catalogs} > <Home name={'React.js'}/> </I18nProvider>, document.getElementById('root'));
Now if we run the app we will see the same screen translated as before. LinguiJS offers a lot of cool features so I urge you to consider it for your next project.
Having explored those three powerful libraries for localization, let’s see one more option that competes equally with the latter.
i18next
i18next aims to target high as it promises to give you a complete solution to localize your product from web to mobile and desktop. i18next is a plugin-based framework and provides you with plugins to:
- detect the user language
- load the translations
- optionally cache the translations
- extension, by using post-processing – e.g. to enable sprintf support.
It works well with small projects and scales when the project has many translation files to load.
Let’s see how we can use it in our React App.
Install it first:
yarn add i18next react-i18next i18next-browser-languagedetector
Next, we need to create a file that will load our i18next configuration and any plugins we need to use. Let’s create a file named i18n.js and put our translations there:
import i18n from 'i18next'; import { initReactI18next } from "react-i18next"; import LanguageDetector from 'i18next-browser-languagedetector'; i18n .use(initReactI18next) .use(LanguageDetector) .init({ // we init with resources resources: { en: { translations: { "Welcome to React.js": "Welcome to React.js", "Declarative": "Declarative", "React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.": "React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.", "Declarative views make your code more predictable and easier to debug.": "Declarative views make your code more predictable and easier to debug." } }, el: { translations: { "Welcome to React.js": "Καλώς 'Ηρθατε στο React.js!", "Declarative": "Δηλωτικό", "React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes.": "To {{name}} καθιστά ανώφελη τη δημιουργία διαδραστικών διεπαφών χρήστη. Σχεδιάστε απλές προβολές για κάθε κράτος στο δικό σας\\n\" +\n" + " εφαρμογή και το React θα ενημερώσει αποτελεσματικά και θα αποδώσει τα σωστά στοιχεία όταν τα δεδομένα σας " + " αλλαγές.", "Declarative views make your code more predictable and easier to debug.": "Οι δηλωτικές προβολές καθιστούν τον κώδικα πιο προβλέψιμο και πιο εύκολο στον εντοπισμό σφαλμάτων." } } }, fallbackLng: 'en', debug: true, // have a common namespace used around the full app ns: ['translations'], defaultNS: 'translations', keySeparator: false, // we use content as keys interpolation: { escapeValue: false, // not needed for react!! formatSeparator: ',' }, react: { wait: true } }); export default i18n;
We needed to put some fallback translations so that we have at least English displayed. Also, we used a LanguageDetector plugin that will use a variety of method to detect the clients supported languages.
Next, we need to update our root component to use this i18n config inside the index.js:
import {I18nextProvider} from 'react-i18next'; import i18n from './i18n'; ReactDOM.render( <I18nextProvider i18n={ i18n }> <App /> </I18nextProvider>, document.getElementById('root'));
So now we are ready to go. Let’s see how we can use it in our main Home.js component:
import React, {Component} from 'react'; import { withTranslation, Trans } from 'react-i18next'; import logo from './logo.svg'; import './Home.css'; class Home extends Component { render() { const { t, i18n } = this.props; const changeLanguage = (lng) => { i18n.changeLanguage(lng); }; return ( <div className="Home"> <div className="Home-header"> <img src={logo} className="Home-logo" alt="logo"/> <h2>{t('Welcome to React.js')}</h2> <button onClick={() => changeLanguage('el')}>el</button> <button onClick={() => changeLanguage('en')}>en</button> </div> <div className="Home-container"> <div className="Home-items"> <div className="Home-item"> <h3 className="focus"> {t('Declarative')} </h3> <div><p> <Trans name={'React.js'}> React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes. </Trans> </p> <p><Trans>Declarative views make your code more predictable and easier to debug.</Trans></p> </div> </div> </div> </div> </div> ); } } // extended main view with translate hoc export default withTranslation('translations')(Home);
We need to wrap our component with the withTranslation HOC passing our namespace. Then we have access to our translations. The Trans component enables you to nest any react content to be translated as one string. Supports both plural and interpolation.
We also added a little language switcher there. If you try to use it you may notice that the switch happens almost instantly and it does not reload the page.
i18next is a huge framework and it supports all the major javascript frameworks over there. I suggest you give it a try and see what a complete solution it is.
Concluding our list of libraries for Reach i18n
When it comes to internationalizing your React apps, it’s important to provide a holistic and future-proof solution that will not inhibit your development efforts. At the end of the day, it’s not just updating some JSON files. Fortunately, Phrase can make your life as a developer easier! To learn more about Phrase, please refer to the Getting Started guide.
If you found these libraries helpful, stay tuned for more on our blog because we still have some substantial answers to deliver. Internationalization has a lot of factors you need to consider to provide the best locale experience for your ever-growing international user base.
Comments
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK