功能特性
国际化
使用 next-intl 实现多语言支持
国际化
Codofly Template 使用 next-intl 提供完整的国际化支持,目前支持英文和简体中文。
next-intl 配置
基础配置
title="i18n.ts"
import { notFound } from 'next/navigation'
import { getRequestConfig } from 'next-intl/server'
// 支持的语言列表
export const locales = ['en', 'zh'] as const
export type Locale = typeof locales[number]
export default getRequestConfig(async ({ locale }) => {
// 验证语言是否支持
if (!locales.includes(locale as any)) notFound()
return {
messages: (await import(`./messages/${locale}.json`)).default
}
})
中间件配置
title="middleware.ts"
import createMiddleware from 'next-intl/middleware'
import { locales } from './i18n'
export default createMiddleware({
locales,
defaultLocale: 'en',
localePrefix: 'always'
})
export const config = {
matcher: ['/((?!api|_next|_vercel|.*\\..*).*)']
}
翻译文件管理
英文翻译
title="messages/en.json"
{
"Common": {
"save": "Save",
"cancel": "Cancel",
"delete": "Delete",
"edit": "Edit",
"loading": "Loading...",
"error": "An error occurred"
},
"Navigation": {
"home": "Home",
"dashboard": "Dashboard",
"teams": "Teams",
"settings": "Settings",
"signIn": "Sign In",
"signOut": "Sign Out"
},
"HomePage": {
"title": "Build AI SaaS Apps Fast",
"subtitle": "Everything you need to launch your AI-powered SaaS",
"getStarted": "Get Started",
"learnMore": "Learn More"
},
"Features": {
"ai": {
"title": "Multi-AI Integration",
"description": "Support for OpenAI, Anthropic, and Google models"
},
"auth": {
"title": "User Authentication",
"description": "Complete auth system with social login"
},
"teams": {
"title": "Team Collaboration",
"description": "Built-in team management and sharing"
}
}
}
中文翻译
title="messages/zh.json"
{
"Common": {
"save": "保存",
"cancel": "取消",
"delete": "删除",
"edit": "编辑",
"loading": "加载中...",
"error": "发生错误"
},
"Navigation": {
"home": "首页",
"dashboard": "仪表盘",
"teams": "团队",
"settings": "设置",
"signIn": "登录",
"signOut": "退出"
},
"HomePage": {
"title": "快速构建 AI SaaS 应用",
"subtitle": "启动 AI 驱动的 SaaS 所需的一切",
"getStarted": "开始使用",
"learnMore": "了解更多"
},
"Features": {
"ai": {
"title": "多 AI 模型集成",
"description": "支持 OpenAI、Anthropic 和 Google 模型"
},
"auth": {
"title": "用户认证",
"description": "完整的认证系统,支持社交登录"
},
"teams": {
"title": "团队协作",
"description": "内置团队管理和共享功能"
}
}
}
多语言路由
根布局
title="app/[locale]/layout.tsx"
import { NextIntlClientProvider } from 'next-intl'
import { getMessages } from 'next-intl/server'
import { setRequestLocale } from 'next-intl/server'
import { locales } from '@/i18n'
export function generateStaticParams() {
return locales.map((locale) => ({ locale }))
}
interface Props {
children: React.ReactNode
params: { locale: string }
}
export default async function LocaleLayout({
children,
params: { locale }
}: Props) {
// 启用静态渲染
setRequestLocale(locale)
const messages = await getMessages()
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
)
}
页面国际化
title="app/[locale]/page.tsx"
import { useTranslations } from 'next-intl'
import { setRequestLocale } from 'next-intl/server'
interface Props {
params: { locale: string }
}
export default function HomePage({ params: { locale } }: Props) {
setRequestLocale(locale)
const t = useTranslations('HomePage')
return (
<div className="text-center">
<h1 className="text-4xl font-bold">{t('title')}</h1>
<p className="text-xl text-gray-600 mt-4">{t('subtitle')}</p>
<div className="mt-8 space-x-4">
<button className="bg-blue-500 text-white px-6 py-2 rounded">
{t('getStarted')}
</button>
<button className="border border-gray-300 px-6 py-2 rounded">
{t('learnMore')}
</button>
</div>
</div>
)
}
语言切换
语言切换组件
title="components/language-switcher.tsx"
'use client'
import { useLocale } from 'next-intl'
import { usePathname, useRouter } from 'next/navigation'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
const languages = [
{ code: 'en', name: 'English', flag: '🇺🇸' },
{ code: 'zh', name: '简体中文', flag: '🇨🇳' }
]
export function LanguageSwitcher() {
const locale = useLocale()
const router = useRouter()
const pathname = usePathname()
const handleLanguageChange = (newLocale: string) => {
// 移除当前语言前缀
const pathWithoutLocale = pathname.replace(`/${locale}`, '') || '/'
// 添加新语言前缀
const newPath = `/${newLocale}${pathWithoutLocale}`
router.push(newPath)
}
return (
<Select value={locale} onValueChange={handleLanguageChange}>
<SelectTrigger className="w-40">
<SelectValue>
{languages.find(lang => lang.code === locale)?.flag}{' '}
{languages.find(lang => lang.code === locale)?.name}
</SelectValue>
</SelectTrigger>
<SelectContent>
{languages.map(language => (
<SelectItem key={language.code} value={language.code}>
{language.flag} {language.name}
</SelectItem>
))}
</SelectContent>
</Select>
)
}
导航栏集成
title="components/navbar.tsx"
import { useTranslations } from 'next-intl'
import { LanguageSwitcher } from './language-switcher'
import { Link } from '@/navigation'
export function Navbar() {
const t = useTranslations('Navigation')
return (
<nav className="flex items-center justify-between p-4">
<div className="flex space-x-6">
<Link href="/">{t('home')}</Link>
<Link href="/dashboard">{t('dashboard')}</Link>
<Link href="/teams">{t('teams')}</Link>
</div>
<div className="flex items-center space-x-4">
<LanguageSwitcher />
<Link href="/auth/signin">{t('signIn')}</Link>
</div>
</nav>
)
}
内容本地化
日期和时间
title="lib/date-formatter.ts"
import { useLocale } from 'next-intl'
export function useDateFormatter() {
const locale = useLocale()
const formatDate = (date: Date) => {
return new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(date)
}
const formatDateTime = (date: Date) => {
return new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
}).format(date)
}
return { formatDate, formatDateTime }
}
数字格式化
title="components/price-display.tsx"
import { useLocale } from 'next-intl'
interface PriceDisplayProps {
amount: number
currency?: string
}
export function PriceDisplay({ amount, currency = 'USD' }: PriceDisplayProps) {
const locale = useLocale()
const formatPrice = (amount: number, currency: string) => {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency
}).format(amount)
}
return <span>{formatPrice(amount, currency)}</span>
}
复数处理
title="components/message-count.tsx"
import { useTranslations } from 'next-intl'
interface MessageCountProps {
count: number
}
export function MessageCount({ count }: MessageCountProps) {
const t = useTranslations('Messages')
return (
<span>
{t('count', { count })}
</span>
)
}
对应的翻译文件:
{
"Messages": {
"count": {
"0": "No messages",
"1": "1 message",
"other": "{count} messages"
}
}
}
动态导入翻译
按需加载翻译
title="lib/translations.ts"
export async function loadTranslations(locale: string, namespace: string) {
try {
const messages = await import(`@/messages/${locale}/${namespace}.json`)
return messages.default
} catch (error) {
console.warn(`Failed to load ${namespace} translations for ${locale}`)
return {}
}
}
分离翻译文件
messages/
├── en/
│ ├── common.json
│ ├── navigation.json
│ ├── auth.json
│ └── dashboard.json
└── zh/
├── common.json
├── navigation.json
├── auth.json
└── dashboard.json
添加新语言
步骤
- 添加语言到配置
title="i18n.ts"
export const locales = ['en', 'zh', 'ja'] as const // 添加日语
- 创建翻译文件
title="messages/ja.json"
{
"Common": {
"save": "保存",
"cancel": "キャンセル",
"delete": "削除"
}
}
- 更新语言切换器
title="components/language-switcher.tsx"
const languages = [
{ code: 'en', name: 'English', flag: '🇺🇸' },
{ code: 'zh', name: '简体中文', flag: '🇨🇳' },
{ code: 'ja', name: '日本語', flag: '🇯🇵' } // 添加日语
]
服务端组件翻译
API 响应本地化
title="app/api/messages/route.ts"
import { getTranslations } from 'next-intl/server'
export async function POST(request: Request) {
const { locale } = await request.json()
const t = await getTranslations({ locale, namespace: 'API' })
try {
// 处理逻辑
return NextResponse.json({
message: t('success')
})
} catch (error) {
return NextResponse.json({
error: t('error')
}, { status: 500 })
}
}
最佳实践
1. 翻译键命名
// ✅ 好的命名
"Auth.SignIn.title"
"Dashboard.Stats.users"
"Teams.Members.invite"
// ❌ 避免的命名
"text1"
"message"
"button"
2. 翻译文件结构
{
"ComponentName": {
"title": "标题",
"description": "描述",
"actions": {
"save": "保存",
"cancel": "取消"
}
}
}
3. 处理缺失翻译
title="lib/fallback-translations.ts"
export function withFallback(key: string, fallback: string) {
const t = useTranslations()
try {
return t(key)
} catch {
return fallback
}
}
性能优化
翻译预加载
title="app/[locale]/loading.tsx"
import { getMessages } from 'next-intl/server'
export default async function Loading({ params: { locale } }) {
// 预加载翻译
await getMessages()
return <div>Loading...</div>
}
静态生成
title="next.config.js"
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
ppr: true, // 启用部分预渲染
},
}
module.exports = nextConfig
查看 next-intl 文档 了解更多高级功能。
Assistant
Responses are generated using AI and may contain mistakes.