Multilingual (Internationalization) ReactJs with Server side rendering

Giving multilingual (Internationalization) or localization using Reactjs is much more simpler than you think. I'am integrating multilingual support with relay and react-router covering all the facts that you needed whether it is larger or smaller app.

Introduction

Before you continue forward read this to get an idea on how localization support is integrated with Reactjs.

  • No need to pass translations from one component to another via props.
  • All the translations are centralized and maintained using a single JSON file.
  • User selected language automatically set to all components and translated to selected language.
  • Integrated with react-intl library because of following features and it is the best available option that I found.
    • Display numbers with separators.
    • Display dates and times correctly.
    • Display dates relative to "now".
    • Pluralize labels in strings.
    • Support for 150+ languages.
    • Runs in the browser and Node.js.
    • Built on standards.
  • Support server side rendering (Isomorphic or Universal rendering).
  • Integrating polyfills.

Step 1: Setup routing structure to support multilingual.

You can use your own routing mechanism. I choose language type to be set on beginning of the url as:
www.aztutorialz.com/en/login
www.aztutorialz.com/fr/login
www.aztutorialz.com/ja/login

lets setup react-router to support those URLstructure.

const ViewerQueries = {
  viewer: () => Relay.QL`query { blogEntry(locale: $language) }`
}
export default [
      {
        path: '/',
        component: Index,
        queries: ViewerQueries,
        indexRoute: {
            component: HomePage,
            queries: ViewerQueries,
            prepareParams: () => ({language: null}),
        },
        childRoutes: [
          {
            path: "/:language",
            component: HomePage,
            queries: ViewerQueries,
            prepareParams: prepareParams,
          },
          {
            path: "/:language/test",
            component: TestPage,
            queries: ViewerQueries,
            prepareParams: prepareParams,
          },
        ],
    },
];
function prepareParams(params) {
  //If url is in intended format
  if( params.language == "en" 
      || params.language == "ja"
      || params.language == "fr"
    ){
    return {
      ...params
    };
  }else{//If url is invalid
    let tmpParam = params;
    tmpParam.language = "en"
    //redirect user to default english if invalid url received
    if(typeof(window)!=="undefined"){
        window.location = "/en"
    }
    //only for universal reactjs 
    //If url params are incorrect set to default value inorder generate page from server side
    return{
      ...tmpParam
    }
  }
};

"ViewerQueries" is related to relay. In that relay query you can see that "locale" argument was passed to "blogEntry". This is done in order support server side rendering. In GraphQl always check for "locale" argument and passed related content according to value passed "locale" argument. "$language" variable is same as the router variable. This is done in order to match URL and server side rendering languages.

Router array is similar to react-router server side rendering example. "prepareParams" function was used to check whether URL contain valid localization variable. If not pass default value.

Step 2: Apply translations settings

Now let's create "index.js" this is the entry file or all other components are linked from this file. Translations settings are applied to this component.

Let's install react-intl libarary
npm install react-intl --save
#index.js
import {injectIntl, IntlProvider, FormattedMessage} from 'react-intl';
import messages from "../locale/index.js"
class IndexPage extends Component{
  constructor(props) {
    super(props)
  }
  render(){
    return(
        <IntlProvider locale="en" messages="en">
            <MyMainComponent/>    
        </IntlProvider>
      )
  }
}

Wrap your main react component with "IntlProvider" as show above. Provide current language to "locale"  and "message" attributes of "IntlProvider" component. Later in this article I will show you how to create translation file and use translations inside react components. Not only that, mapping URL language value with relay to get language related data from server side too. In order to make this simple, part by part will be explained. Feel free to ask questions in comment sections.

Step 3: Create translation files

In above #index.js file you may notice locale file was import and not created that file yet. Let's create translations file. This is much more simpler than you think. Used simple JSON format.

import {addLocaleData} from 'react-intl';
import en from 'react-intl/locale-data/en';
import fr from 'react-intl/locale-data/fr';
import es from 'react-intl/locale-data/es';
addLocaleData([...en, ...fr, ...es]);
const app = {
    "en": {
      welcome:"Welcome to this wonderful world"
      bye:"see you soon. bye"
    },
    "ja": {
      welcome:"この素晴らしい世界へようこそ"
      bye:"また近いうちにお会いしましょう。バイ"
    },
    "fr":{
      welcome:"Bienvenue dans ce merveilleux monde"
      bye:"à bientôt. au revoir"
    }
}
export default function messages(lang){
  return app[lang]
}

Import required language types using their standard language short form. "welcome" and "bye" are translation ids that we are going to be used in react components.

Adding translations to react components

import {FormattedMessage} from "react-intl"
import React from "react"
class HomePage extends React.Component{
  constructor(props) {
    super(props)
    this.setState({
      byeMsg: "Bye...",
      isByeVisible:false
    })
  }
  //Get translation intl using react contextTypes
  static contextTypes = {
    intl : React.PropTypes.object.isRequired,
  }
  render(){
    const {isByeVisible, byeMsg} = this.state
      return (
        <div>
          {
            (isByeVisible) ?
              <h2> {this.state.byeMsg}</h2>
              :
              <div>
                {/* Getting translation value as react component */}
                <h2> <FormattedMessage id="welcome" /> </h2>
              </div>
          }
          <button onClick = {e => this.getTranslationValue()}> Say Bye... </button>  
        </div>
      )
  }
  getTranslationValue(){
    //Getting translation value into state
    this.setState({
      byeMsg: this.context.intl.formatMessage({id:"bye"})
      isByeVisible:true
    })
  }
}

"HomePage" is a react component inside the main component "IndexPage". So translations can be applied to any component inside the main component because we wrap main component with ''IntlProvider". Here translations are show in 2 ways. Because using <FormattedMessage/> will insert <span> tags between your translation string. Ex: <Span> bye </span>. When you want only the string in order to process it you have to use "intl" prop. Instead of passing "intl" props from component to component you can use react context Types to get "intl" variable to any react component inside main component.

Later I will show you how to use date time, formatted strings. plural strings and etc.

How to remove <span> tags in "formattedMessage" of react-intl?

Get "intl" props using react context type. A full example is show above.

//Get intl prop
static contextTypes = {
intl: React.PropTypes.object.isRequired
}
//get translation
this.context.intl.formattedMessage({id:"bye"})

Add Polyfills to reactjs application using webpack

First install "babel-polyfill" pakage using npm and add it to final "app.js" created using webpack.

1. Install "babel-polyfill" package

 npm install --save-dev babel-polyfill 

2. Add polyfill to final JavaScript file using webpack.

Add bellow code to "entry" array of Webpack config file.

entry: [
      'babel-polyfill',
       ...
    ],

Add a Comment

You have to agree to the comment policy.