DEV Community

Cover image for Binding translations to views
Abhijeet Yadav
Abhijeet Yadav

Posted on

Binding translations to views

This post is last in the series of react i18n integration.

In the previous post we went over how our TranslationServiceProvider exposes API method for our components to consume.

Now a ideal place for TranslationServiceProvider would be inside the root component. When our application is getting initialised, react-i18n exposes ready boolean flag which can be accessed using the useTranslation hook.

import React from "react";
import "./styles.css";
import "antd/dist/antd.css";
import { Spin } from "antd";
import { useTranslation } from "react-i18next";
import { Router } from "react-router-dom";
import { TranslationServiceProvider } from "./providers/TranslationServiceProvider";
import { AppRoutes } from "./routes/index";
import { createHashHistory } from "history";
import "./i18n";

const BrowserHistory = createHashHistory();
export default function App() {
  const { ready } = useTranslation();
  return (
    <>
      {!ready ? (
        <Spin className="suspense-spinner" size="large" />
      ) : (
        <TranslationServiceProvider>
          <Router history={BrowserHistory}>
            <AppRoutes />
          </Router>
        </TranslationServiceProvider>
      )}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Once we have encapsulated our main component with TranslationServiceProvider we should now be able to access all the API method provided by TranslationServiceProvider.

If you remember in the previous post I'd mentioned different ways to consume provider methods for class based and functional component. We will have a look at those in this post.

when we say we want to translate textual contents embedded inside our components, we essentially mean to say, if there's any way can link or bind the textual content inside the component with the one mentioned in the .json file.

react-i18n let's us do that with a set of helper function namely

  • t (t short form for translate)
  • <Trans></Trans> (to translate contents with html tag)
  • <Translation></Translation> (comes handy when translating a standalone texts that are not inside any components)

Let's have a look how we'd inject this helper methods in our class and functional component

import { withTranslation } from 'react-i18next';
import { TranslationServiceHelper } from 'HOC/TranslationServiceHelper';
class ToBeTranslated extends React.pureComponent{
  render() {
    const { t, currentLanguage } = this.props;
    return (
        <p>{t('helloworld')}</p>
    )
  }
}

ToBeTranslated = withTranslation()(ToBeTranslated);
export default compose(TranslationServiceHelper)(ToBeTranslated);
Enter fullscreen mode Exit fullscreen mode

In the above example if you can see () empty parenthesis adjacent to withTranslation. The empty parentheses is where you specify which the namespace should the component look into for resolving the translations. if no namespace is mentioned in withTranslation, default namespace is used for lookup.

Alternatively we can also define the namespace to look for inside the t helper function.

The helloworld inside the t helper function is the key inside the json file that binds the translated content.

render() {
    const { t, currentLanguage } = this.props;
    return (
        <p>{t('home:helloworld')}</p>
    )
  }
Enter fullscreen mode Exit fullscreen mode

The above is how you'd for class components, what if I have a functional component. Well, in that case we can make use of useTranslation hook.

import { useTranslation } from 'react-i18next';
const ToBeTranslated = ({}) => {
 const { t } = useTranslation('home');
 return (
  <p>{t('helloworld')}</p>
 )
}
export default ToBeTranslated
Enter fullscreen mode Exit fullscreen mode

The t helper function suffices for only textual content, what if you have to translate below content.

<div>
 It's
 <span style={{color:'red'}}>free</span>
 Forever!
</div>
Enter fullscreen mode Exit fullscreen mode

Translating the above content is one thing, but how do we define keys for above contents inside json file

"key": "Sus <1>GRATIS</1> Siempre!"
Enter fullscreen mode Exit fullscreen mode

see how <1></1> is substituted instead of . For every html tag we have incrementing numbers inside the json file. <1></1>, <2></2>.

For translation we can use Trans component provided by react-i18n.

<Trans i18nKey="key">
  {"It's"}
  <span style=style={{color:'red'}}>FREE </span>
  Forever!
</Trans>
Enter fullscreen mode Exit fullscreen mode

Bonus

If you plan to scale i18n in your application by including multiple locales, you may wish to load translation based on route. Instead of downloading all the translations in one go it makes sense to download them based on routes.

Let's start by adding useEffect to watch for location

useEffect(() => {
    const pathNameTerms = locationPath.includes("/terms");
    const pathNameHome = locationPath.includes("/home");
    if (pathNameTerms) {
      props.loadNameSpaces("terms");
    } else if (pathNameHome) {
      props.loadNameSpaces("home");
    }
}, [props.location]);
Enter fullscreen mode Exit fullscreen mode

The above peace of code watched for route changes and loads appropriate namespace. It also changes the default namespace to terms and home respectively.

That's it for this series. You can see the above lessons in action below. You can also find the github repo containing all codes mentioned in this series here

Discussion (0)