May 22, 2023

How to implement internationalization in a Next.js application that’s using the App Router

Make your React application more accessible and reach new markets with internationalization (i18n).

As the world becomes more globalized, it's increasingly important for web developers to build applications that can cater to users from different countries and cultures. One of the key ways to achieve this is through internationalization (i18n), which allows you to adapt your application to different languages, currencies, and date formats.

In this blog post, we'll explore how to add internationalization to your React Next.js application, with server rendering. TL;DR: See the full example here.

This guide is for Next.js applications that’s using the App Router.
If you are using the Pages Router, see this guide instead.

Step 1: Install a i18n library

To implement internationalization in your Next.js application, we’ll first choose an i18n library. There are several popular libraries, including next-intl. In this example, however, we'll be using TacoTranslate.

TacoTranslate automatically translates your strings to any language using cutting-edge AI, and frees you from the tedious management of JSON files.

Let’s install it using npm in your terminal:

npm install tacotranslate

Step 2: Create a free TacoTranslate account

Now that you’ve got the module installed, it’s time to create your TacoTranslate account, a translation project, and associated API keys. Create an account here. It’s free, and doesn’t require you to add a credit card.

Within the TacoTranslate application UI, create a project, and navigate to its API keys tab. Create one public read key, and one secret read/write key. Save them as environment variables. For example, you could add them to a .env file in the root of your project.


Be sure to never leak the secret read/write API key to client side production environments.

Step 3: Setting up TacoTranslate

To integrate TacoTranslate with your application, you’ll need to create a client using the API keys from earlier. For example, create a file named /utilities/tacotranslate.ts.

import createTacoTranslateClient from 'tacotranslate';

export const defaultLocale =

const tacoTranslate = createTacoTranslateClient({
		process.env.TACOTRANSLATE_IS_PRODUCTION === 'true'
			? defaultLocale
			: undefined,

export default tacoTranslate;

export async function getLocales() {
	return tacoTranslate.getLocales().catch((error) => {
		return [defaultLocale];

We’ll be automatically defining TACOTRANSLATE_API_KEY and TACOTRANSLATE_PROJECT_LOCALE shortly.

Creating the client in a separate file makes it easy to use again later. getLocales is just an utility function with some built-in error handling. Now, create a file named /components/translation-provider.tsx, where we’ll implement the TranslationProvider.

'use client';

import React, {type ReactNode} from 'react';
import {
	type TranslationContextProperties,
} from 'tacotranslate/react';
import tacoTranslate from '@/utilities/tacotranslate';

export default function ClientTranslationProvider({
}: TranslationContextProperties & {
	readonly children: ReactNode;
}) {
	return (

Note the 'use client'; indicating that this is a client component.

With the context provider now ready to go, create a file named /app/[locale]/layout.tsx, the root layout in our application. Note that this path has a folder utilizing Dynamic Routes, where [locale] is the dynamic parameter.

import React, {type ReactNode} from 'react';
import {rightToLeftLocaleCodes} from 'tacotranslate';
import {getOrigin} from 'tacotranslate/next';
import ClientTranslationProvider from '@/components/translation-provider';
import tacoTranslate, {getLocales} from '@/utilities/tacotranslate';

export async function generateStaticParams() {
	const locales = await getLocales();
	return locales.map((locale) => ({locale}));

type RootLayoutParameters = {
	readonly params: {locale: string};
	readonly children: ReactNode;

export default async function RootLayout({
	params: {locale},
}: RootLayoutParameters) {
	const origin = getOrigin();
	const direction = rightToLeftLocaleCodes.includes(locale) ? 'rtl' : 'ltr';
	const translations = await tacoTranslate
		.getTranslations({locale, origin})
		.catch((error) => {
			return {};

	return (
		<html lang={locale} dir={direction}>

The first thing to note here is that we’re using our Dynamic Route parameter [locale] to fetch translations for that language. Additionally, generateStaticParams is making sure all the locale codes you have activated for your project are pre-rendered.

Now, let’s build our first page! Create a file named /app/[locale]/page.tsx.

'use client';

import React from 'react';
import {Translate} from 'tacotranslate/react';

export const revalidate = 60;
export default async function Page() {
	return (
		<Translate string="Hello, world!" />

Note the revalidate variable that tells Next.js to re-build the page after 60 seconds, and keep your translations up-to-date.

Step 4: Implementing server rendering

While client side rendering works just fine, TacoTranslate supports server side rendering. This vastly improves the user experience by showing translated content immediately, instead of a flash of untranslated content first. Additionally, we can skip network requests on the client, because we already have the translations we need for the page the user is viewing.

We’ll start by adding two more environment variables into .env.


TACOTRANSLATE_WEBSITE_URL should be set to the production domain of your current project. This will make sure your strings are created for your production origin. If this is set to anything else, you’ll notice translations don’t work like expected. TACOTRANSLATE_DEFAULT_LOCALE ensures your React application doesn’t stop working if translations for some reason can’t be fetched. All available locale codes can be seen in the TacoTranslate dashboard.

To set up server side rendering, create or modify /next.config.js:

const {default: createTacoTranslateClient} = require('tacotranslate');

module.exports = async () => {
	const publicApiKey = process.env.TACOTRANSLATE_PUBLIC_API_KEY;
	const secretApiKey = process.env.TACOTRANSLATE_SECRET_API_KEY;
	const {getLocales} = createTacoTranslateClient({apiKey: secretApiKey});
	const locales = await getLocales().catch((error) => {
		return [process.env.TACOTRANSLATE_DEFAULT_LOCALE];

	const [projectLocale] = locales;
	const isProduction =
		process.env.TACOTRANSLATE_ENV === 'production' ||
		process.env.VERCEL_ENV === 'production' ||
		(!(process.env.TACOTRANSLATE_ENV || process.env.VERCEL_ENV) &&
			process.env.NODE_ENV === 'production');

	return {
		env: {
			TACOTRANSLATE_API_KEY: isProduction ? publicApiKey : secretApiKey,

There are a few things to note here. First, we’re creating a TacoTranslate client with a secret read/write (secret) API key. Then, we’re fetching all the languages you have activated for your translation project. The first string in this response array will be the origin locale code you set for the project on creation.

Identifying whether or not your application is in a production environment is crucial. Modify the isProduction check to suit your setup. If your application is running in a local, test, or staging environment, we should use the read/write (secret) API key to make sure new strings are sent for translation. However, if your application is running in a production environment, we should only be sending the read (public) API key.

To ensure routing and redirection works as expected, we’ll need to create a file named /middleware.ts. Using Middleware, we can redirect users to pages presented in their preferred language.

import {type NextRequest} from 'next/server';
import {middleware as tacoTranslateMiddleware} from 'tacotranslate/next';
import tacoTranslate from '@/utilities/tacotranslate';

export const config = {
	matcher: ['/((?!api|_next|favicon.ico).*)'],

export async function middleware(request: NextRequest) {
	return tacoTranslateMiddleware(tacoTranslate, request);

Make sure to set up the matcher in accordance with Next.js Middleware documentation.

On the client, you can alter the locale cookie to change what the user’s preferred language is. Please see the complete example code for ideas on how to do this!

Step 5: Deploy and test!

We’re done! Your React application will now be translated automatically when you add any strings to a Translate component. Note that only environments with read/write permissions on the API key will be able to create new strings to be translated. We recommend having a closed and secured staging environment where you can test your production application with an API key like that, adding new strings before going live. This will prevent anyone anyone from stealing your secret API key, and potentially bloating your translation project by adding new, unrelated strings.

Be sure to check out the complete example over at our GitHub profile. There, you’ll find an example of how to implement TacoTranslate with the Pages Router as well! If you have any problems, feel free to reach out.We’re more than happy to help.

TacoTranslate lets you automatically localize your React applications quickly and to any language. Get started today!