CategoryTagArticle

admin

I'm a Full-stack developer

Tag

Linked List
Data Structure
Chat GPT
Design Pattern
Microservices
API
AWS CDK
ReactJS
AWS Lightsail
Flutter Mobile
Next.js 14 App Router Localization with next-intl
Published date: 07/07/2024

In this article, I will illustrate how to implement next-intl localization in a Next.js 14 application. 

Localization is a crucial feature for any application aiming to cater to a global audience, ensuring that users can interact with content in their preferred language.


Overview

  • Packages
  • How to install and configure next-intl?
  • Step 1: Create a fresh Next.js project.
  • Step 2: Install the next-intl package:
  • Step 3: Adding the locale route segment
  • Step 4: Adding the translation message files
  • Step 5: Setting up configuration files
  • Step 6: Create a localization middleware
  • Step 7: How to retrieve translated contents
  • Demo
  • Conclusion


Packages

  • typescript -> ver 5 -> Our programming language
  • next -> ver 14.2.4 -> The full-stack react framework
  • react -> ver 18 -> For building UI
  • next-intl -> ver 3.15.3 -> i18n lib
  • rtl-detect -> ver 1.1.2 -> For detecting right-to-left language
  • tailwindcss -> ver 3.4.1 -> For styling
  • mui/material -> ver 5.15.20 -> For building components


How to install and configure next-intl?

Step 1: Create a fresh Next.js project.

npx create-next-app i18n-next-app
cd i18n-next-app
yarn add next@latest


Step 2: Install the next-intl package:

yarn add next-intl


Step 3: Adding the locale route segment

*** First, we need to follow the documents about locale codes and dynamic route segments.

Next, the subdirectory called [locale] is created under src/app.

*** Note, the module import should be updated if needed.


Step 4: Adding the translation message files

First, create a new directory called locales under src

In this tutorial, we are supporting en-us (English USA) and vi (Vietnamese).



Continue, add the content for translation files

// src/locales/en-us/about.ts

export default {
  title: 'Title',
  description: 'Description',
} as const;


// src/locales/vi/about.ts

export default {
  title: 'Tiêu đề',
  description: 'Miêu tả',
} as const;


// src/locales/vi/index.ts
// src/locales/vi/index.ts

import about from './about';
export default { about };


Step 5: Setting up configuration files

// next.config.mjs

import createNextIntlPlugin from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin();

/** @type {import('next').NextConfig} */
const nextConfig = {};

export default withNextIntl(nextConfig);


Create a new file called i18n.config.ts

import { createSharedPathnamesNavigation } from 'next-intl/navigation';

export const locales = ['en-us', 'vi'] as const;
export type Locale = (typeof locales)[number];

export const localeNames: Record<Locale, string> = {
  'en-us': 'Tiếng anh',
  vi: 'Vietnamese',
};

export const { Link, usePathname, useRouter } = createSharedPathnamesNavigation({ locales });


The next-intl uses the i18n.ts file to load translations.

// src/global.d.ts

type Messages = (typeof import('./locales/en-us'))['default'];

declare interface IntlMessages extends Messages {}


// src/i18n.ts

import { getRequestConfig } from 'next-intl/server';
import { notFound } from 'next/navigation';

import { type Locale, locales } from ' ./i18n.config';

export default getRequestConfig(async ({ locale }) => {
  if (!locales.includes(locale as Locale)) {
    return notFound();
  }

  const messages = await import(`./locales/${locale}/index.ts`);

  return {
    messages: messages.default,
  };
});


Step 6: Create a localization middleware

// src/middlewares/with-localization.ts

import createMiddleware from 'next-intl/middleware';
import { NextMiddleware } from 'next/server';

import { locales } from '../i18n.config';
import { MiddlewareFactory } from './types';

export const withLocalization: MiddlewareFactory = (next: NextMiddleware) =>
  createMiddleware({
    defaultLocale: 'vi',
    locales,
    localeDetection: false,
  });


// src/middleware.ts

import { stackMiddlewares } from './middlewares/stack-middlewares';
import { withLocalization } from './middlewares/with-localization';

export default stackMiddlewares([
  withLocalization,
]);

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


Step 7: How to retrieve translated contents

// src/hooks/useTextDirection.ts

import { useLocale } from 'next-intl';
import { isRtlLang } from 'rtl-detect';

export type TextDirection = 'ltr' | 'rtl';

export default function useTextDirection(): TextDirection {
  const locale = useLocale();

  return isRtlLang(locale) ? 'rtl' : 'ltr';
}


// src/[locale]/layout.tsx

import { Metadata, Viewport } from 'next';
import { NextIntlClientProvider, useMessages } from 'next-intl';
import { unstable_setRequestLocale } from 'next-intl/server';

import { locales } from './src/i18n.config';

import useTextDirection from '@/hooks/useTextDirection';
import './globals.css';

export async function generateMetadata(): Promise<Metadata> {
  return {
    title: {
      default: site.header,
      template: `${site.header} | %s`,
    },
  };
}

export function generateStaticParams() {
  return locales.map((locale) => ({ locale }));
}

export default function RootLayout({
  children,
  params: { locale },
}: Readonly<{
  children: React.ReactNode;
  params: { locale: string };
}>) {
  unstable_setRequestLocale(locale);
  const dir = useTextDirection();
  const messages = useMessages();

  return (
    <html lang={locale} dir={dir} className={combineClasses([roboto.className])}>
      <head></head>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}


// src/app/[locale]/about.page.tsx

import { getTranslations } from 'next-intl/server';

export async function generateMetadata({
  params: { locale },
}: {
  params: { locale: string };
}): Promise<Metadata> {
  const t = await getTranslations({
    locale,
  });

  return {
    title: t('about.title'),
  };
}

export default async function About({ params: { locale } }: { params: { locale: string } }) {
  const t = useTranslations('about');

  return (
    <Layout>
      {t('description')}
    </Layout>
  );
}


Demo

Vietnamese: https://www.dinhthanhcong.info/vi/about




English: https://dinhthanhcong.info/en-us/about



Conclusion

By following these guidelines, you can build a Next.js 14 application that meets the technical requirements of localization and delivers a superior user experience for a diverse audience. 


Good luck to you, I hope this post is of value to you!!!!


Reference Documents:

  1. https://saimana.com/list-of-country-locale-code/
  2. https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes
  3. https://next-intl-docs.vercel.app/docs/getting-started/app-router/without-i18n-routing
Recommend

Flutter push notification
admin14/06/2023

Flutter push notification
Welcome everyone, in this article, we will implement flutter push notification through APNs (Apple Push Notification Service) and FCM (Firebase Cloud Message), and manage messages sent using AWS SNS (Amazon Simple Notification Service).
TypeScript Design Pattern - Adapter
admin08/08/2023

TypeScript Design Pattern - Adapter
This design pattern acts as a bridge between two different interfaces.
Data structure: Singly Linked List
admin07/04/2024

Data structure: Singly Linked List
In this article, I will show you how to set up a singly linked list algorithm in Python.
Newest

TypeScript Design Pattern - Adapter
admin08/08/2023

TypeScript Design Pattern - Adapter
This design pattern acts as a bridge between two different interfaces.
Part 3: React Fragments
admin18/06/2023

Part 3: React Fragments
In this part, I will show you about good benefits when using fragments in React.
How to secure your API gateway
admin17/04/2024

How to secure your API gateway
In this blog, I will cover the 6 methods that technology leaders need to incorporate to secure and protect APIs.
Đinh Thành Công Blog

My website, where I write blogs on a variety of topics and where I have some experiments with new technologies.

hotlinelinkedinskypezalofacebook
DMCA.com Protection Status
Feedback
Name
Phone number
Email
Content
Download app
hotline

copyright © 2023 - AGAPIFA

Privacy
Term
About