• Blog
  • Documentation
  • Courses
  • Changelog
  • AI Starters
  • UI Kit
  • FAQ
  • Supamode
    New
  • Pricing

Launch your next SaaS in record time with Makerkit, a React SaaS Boilerplate for Next.js and Supabase.

Makerkit is a product of Makerkit Pte Ltd (registered in the Republic of Singapore)Company Registration No: 202407149CFor support or inquiries, please contact us

About
  • FAQ
  • Contact
  • Verify your Discord
  • Consultation
  • Open Source
  • Become an Affiliate
Product
  • Documentation
  • Blog
  • Changelog
  • UI Blocks
  • Figma UI Kit
  • AI SaaS Starters
License
  • Activate License
  • Upgrade License
  • Invite Member
Legal
  • Terms of License

Building a Language Switcher for Next.js

Oct 12, 2022

In this article, we create a Language dropdown to switch to another language using Next.js and next-i18n.

next

next-i18n-next is a popular Next.js library for managing translations and i18n in your application or website.

In this article, I want to show you how I made a dynamic language switcher for the Makerkit Next.js template and how you can copy and reuse it in your own applications.

My goal was to automatically load, display and translate the available languages based on the Next.js i18n configuration: this is finally possible thanks to the fantastic Intl Browser API.

For example, let's assume the below is your Next.js i18n configuration:

next-i18next.config.js
const config = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'it'],
},
fallbackLng: {
default: ['en'],
},
localePath: resolve('./public/locales'),
};

The component automatically renders a dropdown with two languages: Italian and English. Furthermore, the language names will be translated into the currently selected language.

Building a Language Selector for Next.js

Let's name our component LanguageSwitcher. This component will also emit a callback with the selected language to its parent, which can be helpful if you want to persist the choice somewhere.

tsx
const LanguageSwitcher: React.FC<{
onChange?: (locale: string) => unknown;
}> = ({ onChange }) => {}
export default LanguageSwitcher;

Let's define some of the constants we're going to use:

tsx
const { i18n } = useTranslation();
const { language: currentLanguage } = i18n;
const router = useRouter();
const locales = router.locales ?? [currentLanguage];

Thanks to router.locales, we can retrieve the languages added to the Next.js configuration. This helps us make the component configuration-less.

Next, we're going to instantiate an Intl.DisplayNames instance: this powerful API will return the language name in the selected language:

tsx
const languageNames = useMemo(() => {
return new Intl.DisplayNames([currentLanguage], {
type: 'language',
});
}, [currentLanguage]);

We can use the Intl.DisplayNames API in the following way:

tsx
console.log(
languageNames.of(lang)
)

For example, if currentLanguage is English and lang is it (Italian), the above will display Inglese, English in Italian.

Let's now define what happens when a language is switched:

  1. Using router.push, we can pass the selected locale. This will cause Next.js to re-render the page with the new locale's translations
  2. We emit the onChange callback if it's passed from the parent component
  3. We set the local state with setValue
tsx
const [value, setValue] = useState({
value: i18n.language,
label: capitalize(languageNames.of(currentLanguage) ?? currentLanguage),
});
const switchToLocale = useCallback(
(locale: string) => {
const path = router.asPath;
return router.push(path, path, { locale });
},
[router]
);
const languageChanged = useCallback(
async (option: ListBoxOptionModel<string>) => {
setValue(option);
const locale = option.value;
if (onChange) {
onChange(locale);
}
await switchToLocale(locale);
},
[switchToLocale, onChange]
);

And now it's time to render our component with a selector element: you can use your own, the native HTML select, or Makerkit's ListBox:

tsx
return (
<ListBox value={value} setValue={languageChanged}>
{locales.map((locale) => {
const label = capitalize(languageNames.of(locale) ?? locale);
const option = {
value: locale,
label,
};
return <ListBoxOption key={locale} option={option} />;
})}
</ListBox>
);

Of course, you should adapt the above to the HTML component you have access to.

Below, you can find a demo:

Full Source Code

Below is the full source code of the component:

tsx
import { useRouter } from 'next/router';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'next-i18next';
import ListBox, { ListBoxOptionModel } from '~/core/ui/ListBox/ListBox';
import ListBoxOption from '~/core/ui/ListBox/ListBoxOption';
const LanguageSwitcher: React.FC<{
onChange?: (locale: string) => unknown;
}> = ({ onChange }) => {
const { i18n } = useTranslation();
const { language: currentLanguage } = i18n;
const router = useRouter();
const locales = router.locales ?? [currentLanguage];
const languageNames = useMemo(() => {
return new Intl.DisplayNames([currentLanguage], {
type: 'language',
});
}, [currentLanguage]);
const [value, setValue] = useState({
value: i18n.language,
label: capitalize(languageNames.of(currentLanguage) ?? currentLanguage),
});
const switchToLocale = useCallback(
(locale: string) => {
const path = router.asPath;
return router.push(path, path, { locale });
},
[router]
);
const languageChanged = useCallback(
async (option: ListBoxOptionModel<string>) => {
setValue(option);
const locale = option.value;
if (onChange) {
onChange(locale);
}
await switchToLocale(locale);
},
[switchToLocale, onChange]
);
return (
<ListBox value={value} setValue={languageChanged}>
{locales.map((locale) => {
const label = capitalize(languageNames.of(locale) ?? locale);
const option = {
value: locale,
label,
};
return <ListBoxOption key={locale} option={option} />;
})}
</ListBox>
);
};
function capitalize(lang: string) {
return lang.slice(0, 1).toUpperCase() + lang.slice(1);
}
export default LanguageSwitcher;
Some other posts you might like...
Jun 9, 2025Claude Code: Build a SaaS with AIThis is a step-by-step guide to building an AI Content Repurposer SaaS by vibe-coding with Claude Code and Makerkit.
Apr 23, 2025Next.js Security: A Comprehensive Guide how to secure your Next.js applicationA comprehensive guide to securing your Next.js application, focusing on practical strategies to protect your application and user data from common vulnerabilities.
Jan 17, 2025Best Practices for Building a SaaS with Windsurf and MakerkitWindsurf is a new AI-powered editor taking the developer experience to the next level. With the new optimized rules for Makerkit, building a SaaS just got a lot easier!
Jan 16, 2025Best Practices for Building a SaaS with Cursor and MakerkitCursor is the hottest AI editor in the market. With the new optimized rules for Makerkit, building a SaaS just got a lot easier!
Dec 26, 2024Choosing the best hosting provider for your Next.js applicationIn this post, we'll show you how to choose the best hosting provider for your Next.js application.
Dec 24, 2024Next.js API Routes: The Ultimate GuideLearn how to build robust, secure, and efficient API endpoints in Next.js. This comprehensive guide will walk you through the critical best practices for creating robust, secure, and efficient API endpoints in Next.js.