全栈开发个人博客02:国际化
cz2025.05.26 06:44

1. 为什么选择next-intl做国际化

  • 支持服务端组件和客户端组件使用
  • 支持构建时静态渲染,有利于SEO
  • 支持动态传参,渲染Html

2. 配置国际化

  • 安装依赖
pnpm add next-intl

使用 App Router with routing 的国际化配置,根据官网配置完成后,文件结构如下:

├── messages
│   ├── en.json
│   └── ...
├── next.config.ts
└── src
    ├── i18n
    │   ├── routing.ts
    │   ├── navigation.ts
    │   └── request.ts
    ├── middleware.ts
    └── app
        └── [locale]
            ├── layout.tsx
            └── page.tsx

3. 重要事项

  • 支持静态渲染

    1.在 layout.tsx 中添加 generateStaticParams 方法,为所有路由开启静态渲染

    // src/app/[locale]/layout.tsx
    import {routing} from '@/i18n/routing';
    export function generateStaticParams() {
      return routing.locales.map((locale) => ({locale}));
    }
    
    1. 在 所有 layout.tsx 和 page.tsx 中使用 setRequestLocale 设置请求的国际化语言
    // src/app/[locale]/layout.tsx
    import { setRequestLocale } from 'next-intl'
    
    setRequestLocale(locale)
    
    // src/app/[locale]/page.tsx
    import { setRequestLocale } from 'next-intl'
    
    setRequestLocale(locale)
    
  • 根域名需要重定向到默认语言

    // src/app/page.tsx
    import { redirect } from 'next/navigation'
    
    export default function RootPage() {
      redirect('/en')
    }
    
  • 切换域名使用i18n自带的router.replace(),保留当前用户访问路径

    // LocaleSwitcherSelect.tsx
    
    'use client'
    import { useParams, usePathname, useRouter } from '@/i18n/navigation'
    import { Locale, routing } from '@/i18n/routing'
    import {
      DropdownMenu,
      DropdownMenuContent,
      DropdownMenuItem,
      DropdownMenuTrigger,
    } from '@/components/ui/dropdown-menu'
    import { Languages } from 'lucide-react'
    import { useLocale, useTranslations } from 'next-intl'
    import { useTransition } from 'react'
    type Props = {
      defaultValue: string
      items: Array<{ value: string; label: string }>
      label: string
    }
    
    function LocaleSwitcherSelect({ defaultValue, items, label }: Props) {
      const [isPending, startTransition] = useTransition()
      const router = useRouter()
      const pathname = usePathname()
      const params = useParams()
    
      function onChange(value: string) {
        const nextLocale = value as Locale
        startTransition(() => {
          router.replace(
            { pathname, params },
            { locale: nextLocale },
          )
        })
      }
    
      return (
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <div className="hover:bg-accent hover:text-accent-foreground cursor-pointer p-2 transition-colors">
              <Languages className="size-4" />
            </div>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end">
            {items.map((item) => (
              <DropdownMenuItem
                key={item.value}
                onClick={() => onChange(item.value)}
              >
                {item.label}
              </DropdownMenuItem>
            ))}
          </DropdownMenuContent>
        </DropdownMenu>
      )
    }
    
    export default function LocaleSwitcher() {
      const t = useTranslations('LocaleSwitcher')
      const locale = useLocale()
    
      return (
        <LocaleSwitcherSelect
          defaultValue={locale}
          items={routing.locales.map((locale) => ({
            value: locale,
            label: t(locale),
          }))}
          label={t('label')}
        />
      )
    }
    
    

Comments