Exposing API using React provider
Abhijeet Yadav
Posted on June 30, 2020
This post is third in the series of react i18n integration.
To summarise the previous post, we added the necessary config to initialise react-i18n.
Now let's see how the provider, using this config, exposes API's that are consumed by our react components.
Our provider should have following functionality
- get all supported locale list
- change language
- load namespace
- query the current language
The
changeLanguage
andloadNameSpaces
function provided byreact-i18n
are asynchronous since it requires making XHR call. To make sure our application is rendered only after the above calls are resolved we maintain a booleanisTReady
and set it accordingly.
Let's look at the API's now. Start by pasting following code.
import React from "react";
import PropType from "prop-types";
import { withTranslation } from "react-i18next";
let TranslationContext;
const { Provider, Consumer } = (TranslationContext = React.createContext({
currentLanguage: "",
isTReady: false,
localeList: [],
setTReady: () => {},
loadNameSpaces: () => {},
changeLanguage: () => {},
hasNameSpaceLoaded: () => {},
setEntityPreferredLocale: () => {}
}));
class TranslationServiceProvider extends React.Component {
constructor(props) {
super(props);
const { tReady } = this.props;
this.state = {
currentLanguage: "",
isTReady: tReady,
localeList: [
{ id: 1, name: "English", locale: "en" },
{ id: 2, name: "Spanish", locale: "es" }
]
};
}
}
Now that we have initialised our state. Let's go ahead and add the languageChanged
subscriber call inside componentDidMount
. The subscriber makes sure to to set isTReady
to false when the locales are being fetched. We also set the currentLanguage
to the one detected by the plugin.
componentDidMount() {
const { i18n } = this.props;
i18n.on("languageChanged", () => {
this.setTReady(false);
});
this.setLang(
i18n && i18n.language && i18n.language.slice(0, 2).toLocaleLowerCase()
);
}
After we are done adding the subscriber, we need API's to changeLanguage
and loadNameSpaces
.
setLang = lang => {
this.setState({
currentLanguage: lang
});
};
setTReady = ready => {
this.setState({
isTReady: ready
});
};
loadNameSpaces = ns => {
const { i18n } = this.props;
this.setTReady(false);
i18n.loadNamespaces(ns).then(() => {
i18n.setDefaultNamespace(ns);
this.setTReady(true);
});
};
changeLanguage = lang => {
const { i18n } = this.props;
this.setTReady(false);
return i18n.changeLanguage(lang).then(() => {
this.setTReady(true);
if (lang) {
this.setLang(lang.toLocaleLowerCase().slice(0, 2));
}
});
};
hasNameSpaceLoaded = ns => {
const { i18n } = this.props;
return i18n.options.ns.indexOf(ns) > -1;
};
setEntityPreferredLocale = locale => {
this.changeLanguage(locale);
};
The above API's takes care of loadingNameSpaces
, changeLanguage
and checking if the namespaces was already loaded using hasNameSpaceLoaded
.
This essentially completed a major chunk of out i18n
application. The only thing left now is to encapsulate our Component
inside TranslationServiceProvider
Our application component subscribes to above API using a HOC.
import React from "react";
import { TranslationConsumer } from "../providers/TranslationServiceProvider";
export const TranslationServiceHelper = Component =>
class extends React.Component {
render() {
return (
<TranslationConsumer>
{context => (
<Component
{...this.props}
{...this.state}
localeList={context.localeList}
currentLanguage={context.currentLanguage}
isTReady={context.isTReady}
loadNameSpaces={context.loadNameSpaces}
changeLanguage={context.changeLanguage}
hasNameSpaceLoaded={context.hasNameSpaceLoaded}
setEntityPreferredLocale={context.setEntityPreferredLocale}
/>
)}
</TranslationConsumer>
);
}
};
In the next post we will take a look at how our components consume provider methods. Since, a component can be a class based or functional, our provider should account for both components.
Find the github repo containing all codes mentioned in this series here
Posted on June 30, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.