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 - Bridge
admin08/08/2023

TypeScript Design Pattern - Bridge
Decouple an abstraction from its implementation so that the two can vary independently.
TypeScript Design Pattern - Proxy
admin11/08/2023

TypeScript Design Pattern - Proxy
Provide a surrogate or placeholder for another object to control access to it.
Design Patterns
admin07/08/2023

Design Patterns
The design pattern does not be a specific programming language. Almost programming languages might apply design patterns that to resolve a problem repeat.
Mới nhất

Part 2: Setup Custom Domain Zone + SSL for Ghost on AWS Lightsail
admin17/06/2023

Part 2: Setup Custom Domain Zone + SSL for Ghost on AWS Lightsail
In this section, I will continue to show you how to point Ghost Instance Static IP to your domain.
Create Project with Express + TypeScript + ESLint + Auto Reload
admin12/06/2023

Create Project with Express + TypeScript + ESLint + Auto Reload
In this article, I introduce to you how to initialize an Express + TypeScript project.
TypeScript Design Pattern - Builder
admin07/08/2023

TypeScript Design Pattern - Builder
TypeScript Design Pattern - Builder
Đ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