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
Top comments (0)