字节笔记本字节笔记本

nextjs14 next-intl 国际化的配置和使用

2024-05-22

本文详细介绍了如何在Next.js 14项目中使用`next-intl`插件进行国际化配置和实现,包括项目目录结构、文件说明及内容、国际化中间件配置、多语言支持、路由处理以及在组件中使用翻译钩子等。

项目目录结构

my-nextjs-project/
├── messages/
│   ├── cn.json
│   ├── de.json
│   ├── en.json
│   ├── es.json
│   ├── fr.json
│   ├── jp.json
│   ├── pt.json
│   ├── ru.json
│   └── tw.json
├── middlewares/
│   └── intlMiddleware.ts
├── app/
│   ├── navigation.ts
│   └── [locale]/
│       └── index.tsx
├── i18n.ts
├── middleware.ts 
├── next.config.mjs
└── tsconfig.json

文件说明和内容

1. messages/

包含各语言的翻译文件。

示例:
messages/en.json

{
  "title": "Welcome",
  "description": "Discover the best AI tools in 2024."
}

messages/cn.json

{
  "title": "欢迎",
  "description": "发现2024年最好的AI工具。"
}

2. middlewares/intlMiddleware.ts

配置国际化中间件。

import createMiddleware from 'next-intl/middleware';

import { localePrefix } from '@/app/navigation';

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

const intlMiddleware = createMiddleware({
  locales,
  defaultLocale: 'en',
  localePrefix,
});

export default intlMiddleware;

  • localePrefix 被用来告诉中间件在处理国际化时如何解析URL中的语言前缀。这样,当用户访问特定的语言版本的页面时,中间件可以正确识别并加载相应的语言资源。

3.middleware.ts

import { type NextRequest } from 'next/server';

import intlMiddleware from './middlewares/intlMiddleware';

export default function middleware(request: NextRequest) {
  return intlMiddleware(request);
}

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

对应的路由中间件 用于处理服务端请求不同的,本质上就是加载国际化语言的静态资源

4. i18n.ts

定义支持的语言并加载翻译消息。

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

export const languages = [
  {
    code: 'en-US',
    lang: 'en',
    label: 'English',
  },
  {
    code: 'ja-JP',
    lang: 'jp',
    label: '日本語',
  },
  {
    code: 'de-DE',
    lang: 'de',
    label: 'Deutsch',
  },
  {
    code: 'es-ES',
    lang: 'es',
    label: 'Español',
  },
  {
    code: 'fr-FR',
    lang: 'fr',
    label: 'Français',
  },
  {
    code: 'pt-BR',
    lang: 'pt',
    label: 'Português',
  },
  {
    code: 'ru-RU',
    lang: 'ru',
    label: 'Русский',
  },
  {
    code: 'zh-CN',
    lang: 'cn',
    label: '简体中文',
  },
  {
    code: 'zh-TW',
    lang: 'tw',
    label: '繁體中文',
  },
];

export const locales = languages.map((lang) => lang.lang);

export default getRequestConfig(async ({ locale }) => {
  console.log("locale: ", locale)
  // Validate that the incoming `locale` parameter is valid
  if (!locales.includes(locale as any)) notFound();

  return {
    messages: (await import(`./messages/${locale}.json`)).default,
  };
});
  • messages 用于后面使用 useMessage 传递对应的国际化语言的对象, 见 app/[locale]/layout.ts

4. next.config.mjs

配置 Next.js 和 next-intl 插件。

import createNextIntlPlugin from 'next-intl/plugin';

const withNextIntl = createNextIntlPlugin({
  locales: ['en', 'cn', 'de', 'es', 'fr', 'jp', 'pt', 'ru', 'tw'],
  defaultLocale: 'en',
});

/** @type {import('next').NextConfig} */
const nextConfig = {
  env: {
    NEXT_BASE_API: process.env.NEXT_BASE_API,
  },
  logging: {
    fetches: {
      fullUrl: process.env.NODE_ENV === 'development',
    },
  },
  compiler: {
    removeConsole: process.env.NODE_ENV === 'production',
  },
  images: {
    unoptimized: true,
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'img.test.ai',
        port: '',
        pathname: '/**',
      },
    ],
  },
  productionBrowserSourceMaps: false,
};

export default withNextIntl(nextConfig);

5. app/[locale]/navigation.ts

创建共享路径导航函数。


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

import { locales } from '@/i18n';

export const localePrefix = 'as-needed';

// eslint-disable-next-line object-curly-newline
export const { Link, redirect, usePathname, useRouter } = createSharedPathnamesNavigation({ locales, localePrefix });

为多语言网站生成每种语言版本的导航链接,同时标识出当前用户所在的语言页面是否为激活状态。

作为构建语言切换功能,让用户可以方便地在不同语言版本之间切换。

在客户端组件中的使用

'use client';

import { useState } from 'react';
import { languages } from '@/i18n';
import { useLocale } from 'next-intl';

import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';

import { usePathname, useRouter } from '../app/navigation';
import Icon from './image/Icon';

export default function LocaleSwitcher() {
  const currentLocale = useLocale();
  const pathname = usePathname();
  const router = useRouter();

  const [localeVal, setLocaleVal] = useState(currentLocale);

  const onValueChange = (newLocale: string) => {
    setLocaleVal(newLocale);
    router.replace(pathname, { locale: newLocale });
  };

  return (
    <Select value={localeVal} defaultValue={currentLocale} onValueChange={onValueChange}>
      <SelectTrigger className='flex h-8 w-[80px] items-center gap-1 rounded-[4px] bg-[#232330] px-2 text-[#FFFFFF66]'>
        <Icon src='/icons/global.svg' />
        <SelectValue placeholder='locale'>{localeVal.toUpperCase()}</SelectValue>
      </SelectTrigger>
      <SelectContent className='bg-[#232330]'>
        {languages.map((language) => (
          <SelectItem value={language.lang} key={language.code} className='hover:cursor-pointer hover:!bg-white/40'>
            {language.label}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  );
}

6. app/[locale]/layout.tsx`

定义根布局组件,使用 NextIntlClientProvider 提供国际化支持。

import { NextIntlClientProvider, useMessages } from 'next-intl';
import './globals.css';


import Loading from './loading';

export default function RootLayout({
  children,
  params: { locale },
}: {
  children: React.ReactNode;
  params: { locale: string };
}) {
  const messages = useMessages();
  console.log('messages:', messages);  //打印输出对应语言的对象
  return (
    <html lang={locale} suppressHydrationWarning className='dark'>
      <body className='relative mx-auto flex min-h-screen flex-col bg-tap4-black text-white'>
        <NextIntlClientProvider locale={locale} messages={messages}>
        	{children}
        </NextIntlClientProvider>
        <SeoScript />
        <GoogleAdScript />
      </body>
    </html>
  );
}

7. 使用 useTranslations 钩子

在需要翻译的组件中使用 useTranslations 钩子。

import { useTranslations } from 'next-intl';

export default function MyComponent() {
  const t = useTranslations('common');

  return (
    <div>
      <h1>{t('title')}</h1>
      <p>{t('description')}</p>
    </div>
  );
}

每个文件的具体作用和使用方法如下:

  1. i18n.ts:定义支持的语言和加载翻译消息。
  2. next.config.mjs:配置 Next.js 和 next-intl 插件。
  3. intlMiddleware.ts:创建国际化中间件,确保每个请求使用正确的语言设置。
  4. utils/navigation.ts:创建共享路径导航函数。
  5. RootLayout.tsx:定义根布局组件,使用 NextIntlClientProvider 提供国际化支持。
  6. 使用 useTranslations 钩子:在组件中获取和使用翻译内容。
  7. 翻译文件:存储不同语言的翻译内容。