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> ); }
每个文件的具体作用和使用方法如下:
i18n.ts
:定义支持的语言和加载翻译消息。next.config.mjs
:配置 Next.js 和next-intl
插件。intlMiddleware.ts
:创建国际化中间件,确保每个请求使用正确的语言设置。utils/navigation.ts
:创建共享路径导航函数。RootLayout.tsx
:定义根布局组件,使用NextIntlClientProvider
提供国际化支持。- 使用
useTranslations
钩子:在组件中获取和使用翻译内容。 - 翻译文件:存储不同语言的翻译内容。