· tutorials · 6 min read
React Internationalization (i18n) using Context API
Learn how to implement the Provider pattern with Context API and see how it can be used for internationalization in a React application.
React’s Context API is a powerful feature that enables the easy sharing of data between components without having to pass down props manually at every level of the component tree.
There are many use cases for using it but one of the most common use cases for context is internationalization (i18n), which allows for easy translation of text in different languages.
In this blog post, we will explore how to implement the Provider pattern with React Context API, and provide a simple example of how it can be used for i18n.
Provider Pattern and React Context API
The Provider pattern is a way of providing data to all components in a React component tree, without having to pass down props manually to every component. Basically, it means avoiding Prop drilling
.
Prop drilling is a situation where data is passed from one component through multiple interdependent components until you get to the component where the data is needed.
To accomplish this we will be using the Context API, as it provides a way to create a global state object that any component in the app can access. The Provider component is used to wrap the entire component tree and provide the global state to all components that need it.
Implementation
Let’s say we have an application that needs to support multiple languages. We can create a context object for i18n, which will contain the language-specific text for the application. We can then create a Provider component that will wrap the entire component tree, and provide the i18n context to all components that need it.
Start with creating a file called i18n-context.tsx
in the root directory, then import the necessary modules for our context.
import React, { createContext, useState, ReactNode } from 'react';
Now for stick typing and to avoid run-time errors, define a Language
type that represents the available languages in our application. Then also define an interface called I18nContextType
which represents the shape of our context store. It contains the current language
selected, a setLanguage
function to update the selected language and an i18n
object which contains the translations for each language.
export type Language = 'en' | 'fr';
export interface I18nContextType {
language: Language;
setLanguage: (language: Language) => void;
i18n: {
[key in Language]: {
greeting: string;
buttonText: string;
};
};
}
Now define the i18n
object that contains the translations for each language or create a seperate constant file and export the i18n object.
const i18n: I18nContextType['i18n'] = {
en: {
greeting: 'Hello',
buttonText: 'Click me',
},
fr: {
greeting: 'Bonjour',
buttonText: 'Cliquez-moi',
},
};
Next create the i18n context using the createContext
function, passing in an object with the default values of language
, setLanguage
, and i18n
.
export const I18nContext = createContext<I18nContextType>({
language: 'en',
setLanguage: () => {},
i18n: i18n,
});
Next let’s create the I18nProvider
component that wraps around our entire application, passing the children
prop. In the component, we use the useState
hook to store the language
and setLanguage
values then we use the I18nContext.Provider
to provide the context to all child components.
interface I18nProviderProps {
children: ReactNode;
}
export const I18nProvider = ({ children }: I18nProviderProps) => {
const [language, setLanguage] = useState<Language>(
(navigator.language.slice(0, 2) as Language) || 'en'
);
return (
<I18nContext.Provider value={{ language, setLanguage, i18n }}>
{children}
</I18nContext.Provider>
);
};
The I18nProvider
component sets the default language to the user’s preferred language by using navigator.language.slice(0, 2)
to extract the first two characters of the user’s language and match it to our defined Language
type. If the user’s language is not supported, it defaults to English.
By wrapping the application with the I18nProvider
, all child components can access the context store using the useContext
hook. They can use the values of language
, setLanguage
, and i18n
to display language-specific text and update the language when the user selects a different one.
Here is how the i18n-context.tsx
will look like after following the above steps:
import React, { createContext, useState, ReactNode } from 'react';
// Declaing the types/interfaces for Context Store
type Language = 'en' | 'fr';
interface I18nContextType {
language: Language;
setLanguage: (language: Language) => void;
i18n: {
[key in Language]: {
greeting: string;
buttonText: string;
};
};
}
// Define language-specific text
const i18n: I18nContextType['i18n'] = {
en: {
greeting: 'Hello',
buttonText: 'Click me',
},
fr: {
greeting: 'Bonjour',
buttonText: 'Cliquez-moi',
},
};
// Create i18n context object
export const I18nContext = createContext<I18nContextType>({
language: 'en',
setLanguage: () => {},
i18n: i18n,
});
interface I18nProviderProps {
children: ReactNode;
}
// Create Provider component
export const I18nProvider = ({ children }: I18nProviderProps) => {
const [language, setLanguage] = useState<Language>(
(navigator.language.slice(0, 2) as Language) || 'en'
);
return (
// Provide i18n context to all child components
<I18nContext.Provider value={{ language, setLanguage, i18n }}>
{children}
</I18nContext.Provider>
);
};
Usage
To consume the i18n context, we use the useContext hook from React. The useContext hook takes the context object created using createContext as an argument and returned the current context value.
Let’s wrap the App
component with the I18nContext.Provider
component.
import * as React from 'react';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { I18nProvider } from './I18nContext';
import App from './App';
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
root.render(
<StrictMode>
<I18nProvider> // Our Provider
<App />
</I18nProvider>
</StrictMode>
);
Now update the App
component with the following code:
import React, { useContext } from 'react';
import { I18nContext } from './I18nContext';
function App() {
const { language, i18n, setLanguage } = useContext(I18nContext);
const toggleLanguage = () => {
setLanguage(language === 'en' ? 'fr' : 'en');
};
return (
<div>
<div>{i18n[language].greeting}</div>
<button onClick={toggleLanguage}>{i18n[language].buttonText}</button>
</div>
);
}
export default App;
The App
component uses the useContext
hook to access the language
, i18n
, and setLanguage
properties from the I18nContext
object.
It then renders a div
with the current greeting based on the current language and a button that allows the user to toggle between the supported languages.
When the user clicks on the button, the toggleLanguage
function is called, which updates the language
property using the setLanguage
function from the context.
The i18n
object is used to retrieve the appropriate greeting and button text for the current language.
Conclusion
The Provider pattern with React Context API is a powerful way to provide a global state to all components in a React component tree. The i18n example above demonstrates how we can use this pattern for internationalization in our applications. By using the Provider pattern, we can avoid the need to pass down props manually at every level of the component tree, which can simplify our code and make it easier to maintain.
Need Help with i18n Implementation?
If you need assistance in implementing internationalization (i18n) for your React or Next.js application, I offer freelance services to help you seamlessly integrate multi-language support into your website or web app.
I can ensure that your i18n implementation is efficient, maintainable, and provides a smooth user experience across different languages and locales.
Whether you’re starting a new project or looking to add i18n to an existing codebase, I can work with you to understand your specific requirements and deliver a tailored solution that meets your needs.
Feel free to reach out to me here - Contact Seerat Awan
Subscribe to my newsletter to get the latest updates on my blog.