Danh mụcThẻBài viết

admin

I'm a Full-stack developer

Thẻ

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
Ngày đăng: 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
Đề xuất

TypeScript Design Pattern - Singleton
admin07/08/2023

TypeScript Design Pattern - Singleton
The singleton ensures only a single install is created in a class and provides a method to access this instance everywhere in a codebase.
JOI - API schema validation
admin12/06/2023

JOI - API schema validation
Data validation is one of topics that I am interesting. I always review my code after developed features or fixed bugs. There are many places where need to validate data, it is really terrible. Some cases, we need to validate data input because ensure the data into API, it will not make any problems to crash system.
Form validator in Flutter with EzValidator
admin04/01/2024

Form validator in Flutter with EzValidator
When I am working on Flutter with form. For ensuring data integrity, and handling user input errors. I want to show an error message below each TextField, Dropdown, Switch, ... if the user does not input or wrong input. The EzValidator help me to resolve this.
Mới nhất

NodeJS Verify and Decode Cognito JWT Tokens
admin12/06/2023

NodeJS Verify and Decode Cognito JWT Tokens
In this article, I will show you how to verify and decode the Cognito JWT Tokens token.
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.
TypeScript Design Pattern - Bridge
admin08/08/2023

TypeScript Design Pattern - Bridge
Decouple an abstraction from its implementation so that the two can vary independently.
Đ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
Góp ý
Họ & Tên
Số điện thoại
Email
Nội dung
Tải ứng dụng
hotline

copyright © 2023 - AGAPIFA

Privacy
Term
About