Back to blog

I18n with Storybook

How to internationalize components with Storybook

loading
Shaun Evening
β€” @Integrayshaun
Last updated:

Supporting multiple languages in one app is tricky. Some languages have lengthy words which cause unexpected text overflow. Some have different locales across regionsβ€” think UK English vs US English. Others are read right-to-left which changes the entire page flow.

Apps for global audiences need internationalization (i18n), the practice of β€œlocalizing” an app for different regions. But internationalizing UIs is tedious. You have to verify each state of your app in every supported locale by manually swapping between language preferences during development.

With Storybook, you can pass arbitrary locales to your components without changing browser preferences and swap locales in a single click. This recipe shows you how.

βž• Expose i18next translations in Storybook
🎁 Wrap stories with the i18next provider
πŸ”˜ Add a locale switcher to the toolbar
πŸ‘ˆ Add right-to-left language support

What we’re building

Most developers use i18next, a popular JavaScript library that lets apps define separate files for each supported locale. It detects a user’s language preferences and region, and only loads the detected locale.

Instead of being passed to components as inputs, the locale is shared globally through context. Let’s use i18next to extend Storybook with a locale switcher in the toolbar to choose which locale is shared with your components.

Follow along using the code examples in the i18next-react GitHub repository.

Switching locale between English, German, and Arabic in Storybook

Prerequisites

Before we begin, ensure that you have a working React app using i18next-react which is set up with Storybook 6.0 or newer. If you need resources to set these up, I’ve included some recommendations below:

Or if you'd prefer a video, check out Chantastic's awesome video on adding i18next to your React app.

1. Expose i18next to Storybook

To make your translations available in your stories, you’ll first need to expose your i18next instance to Storybook. Here’s an example of an i18next instance from the ./src/i18n.js file being used in my React app.

// src/i18n.js

import i18n from 'i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
 
i18n
 .use(Backend) // lazy loads translations from /public/locales
 .use(LanguageDetector) // detect user language
 .init({
   fallbackLng: 'en',
   debug: true,
   interpolation: {
     escapeValue: false,
   },
 });
 
export default i18n;

To expose this instance to Storybook, we can import it into the ./.storybook/preview.js file where Storybook holds its shared story configurations.

// .storybook/preview.js
import i18n from '../src/i18n';

2. Wrap your stories with the i18next provider

Now that Storybook has access to i18next, we need to share that with our stories. To do that we’re going to make a decorator to wrap our stories in.

// .storybook/preview.js
import React, { Suspense } from "react";
import { I18nextProvider } from "react-i18next";
import i18n from '../src/i18n';
 
// Wrap your stories in the I18nextProvider component
const withI18next = (Story) => {
 return (
   // This catches the suspense from components not yet ready (still loading translations)
   // Alternative: set useSuspense to false on i18next.options.react when initializing i18next
   <Suspense fallback={<div>loading translations...</div>}>
     <I18nextProvider i18n={i18n}>
       <Story />
     </I18nextProvider>
   </Suspense>
 );
};
 
// export decorators for storybook to wrap your stories in
export const decorators = [withI18next];

Sweet! Our stories officially have access to our translations. If we change the lng defined in ./src/i18n.js you’ll see your stories reload in the new language.

Manually changing the locale from English to French

3. Add a locale switcher

Hardcoding your locale is annoying and won’t be helpful to anyone viewing your deployed Storybook, so let’s add a locale switcher to the Storybook toolbar. If you want to learn more about switchers, check out Yann Braga’s article on adding a theme switcher.

To do this, we can declare a global variable named locale in .storybook/preview.js and assign it to a list of supported languages to choose from.

// .storybook/preview.js

/* Snipped for brevity */
 
// Create a global variable called locale in storybook
// and add a menu in the toolbar to change your locale
export const globalTypes = {
 locale: {
   name: 'Locale',
   description: 'Internationalization locale',
   toolbar: {
     icon: 'globe',
     items: [
       { value: 'en', title: 'English' },
       { value: 'de', title: 'Deutsch' },
     ],
     showName: true,
   },
 },
};

Looking back at Storybook, we can now see that we have a β€œLocale” switcher added to the toolbar with the options we just set.

The locale switcher in the Storybook toolbar

Now let’s update our decorator to change our locale when we select a new language.

// .storybook/preview.js

/* Snipped for brevity */

const withI18next = (Story, context) => {
 const { locale } = context.globals;
 
 // When the locale global changes
 // Set the new locale in i18n
 useEffect(() => {
   i18n.changeLanguage(locale);
 }, [locale]);
 
 return (
   <Suspense fallback={<div>loading translations...</div>}>
     <I18nextProvider i18n={i18n}>
       <Story />
     </I18nextProvider>
   </Suspense>
 );
};

Voilaβ€” a fully functioning locale switcher for your stories powered by i18next-react!

Switching between English and German using the locale switcher

4. Set document direction

Some languages are not read from left to right like English is. Arabic, for example, is read from right to left. HTML has built-in support for this with the dir attribute.

First of all, let's add Arabic as an option in our locale switcher by adding an object into the items array of our globalTypes.

// .storybook/preview.js

/* Snipped for brevity */
 
// Create a global variable called locale in storybook
// and add a menu in the toolbar to change your locale
export const globalTypes = {
 locale: {
   name: 'Locale',
   description: 'Internationalization locale',
   toolbar: {
     icon: 'globe',
     items: [
       { value: 'en', title: 'English' },
       { value: 'de', title: 'Deutsch' },
       { value: 'ar', title: 'عربي' },
     ],
     showName: true,
   },
 },
};

Using i18next’s dir(lng) function and languageChanged event, we can set the document direction for the selected locale.

// .storybook/preview.js
 
/* Snipped for brevity */
 
// When The language changes, set the document direction
i18n.on('languageChanged', (locale) => {
 const direction = i18n.dir(locale);
 document.dir = direction;
});

Now when we set our storybook to Arabic, the document direction gets set to ”rtl” πŸŽ‰

Switching between English, German, and Arabic with the locale switcher

Wrapping up

Adding a locale switcher into Storybook allows you to check your components with longer strings and even right-to-left languages like Arabic.

If you want to reference my DIY implementation, check out the Storybook example for i18next-react. However, if you’d prefer to have this handled for you, check out Stevensack’s awesome storybook-react-i18next addon.

Want more recipes?

We want to hear from you!

Did you enjoy this recipe? Are there other Storybook integrations that you want to see recipes for?

Tweet at @storybookjs or reach out on the Storybook Discord Server! We can't wait to meet you 🀩

Join the Storybook mailing list

Get the latest news, updates and releases

6,587 developers and counting

We’re hiring!

Join the team behind Storybook and Chromatic. Build tools that are used in production by 100s of thousands of developers. Remote-first.

View jobs

Popular posts

Material UI in Storybook

Three tips to get the most out of Material UI with Storybook
loading
Shaun Evening
Join the community
6,587 developers and counting
WhyWhy StorybookComponent-driven UI
Open source software
Storybook

Maintained by
Chromatic
Special thanks to Netlify and CircleCI