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
changeLanguageandloadNameSpacesfunction provided byreact-i18nare asynchronous since it requires making XHR call. To make sure our application is rendered only after the above calls are resolved we maintain a booleanisTReadyand 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)