📖 核心概念
- i18n核心概念:消息ID、插值、复数
- next-intl与react-i18next
- 路由与语言检测
- 日期、数字、货币格式化
- RTL语言支持
- 翻译工作流与自动化
💻 代码实现
next-intl国际化
✅ typescript
// messages/zh.json
// {
// "common": {
// "hello": "你好,{name}!",
// "items": "{count, plural, =0 {没有项目} =1 {1个项目} other {#个项目}}",
// "currency": "{value, number, :: currency/CNY}"
// },
// "nav": { "home": "首页", "about": "关于", "contact": "联系" },
// "blog": { "title": "博客", "publishedAt": "发布于 {date, date, long}" }
// }
// messages/en.json
// {
// "common": {
// "hello": "Hello, {name}!",
// "items": "{count, plural, =0 {No items} =1 {1 item} other {# items}}",
// "currency": "{value, number, :: currency/USD}"
// },
// "nav": { "home": "Home", "about": "About", "contact": "Contact" },
// "blog": { "title": "Blog", "publishedAt": "Published on {date, date, long}" }
// }
// i18n/request.ts
import { getRequestConfig } from 'next-intl/server';
export default getRequestConfig(async ({ locale }) => ({
locale,
messages: (await import(\`../messages/\${locale}.json\`)).default,
}));
// middleware.ts
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
locales: ['zh', 'en', 'ja'],
defaultLocale: 'zh',
});
export const config = { matcher: ['/((?!api|_next|_vercel|.*\..*).*)'] };
// app/[locale]/layout.tsx
import { NextIntlClientProvider } from 'next-intl';
export default function LocaleLayout({ children, params: { locale } }) {
return (
<html lang={locale} dir={getDirection(locale)}>
<body>
<NextIntlClientProvider locale={locale}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
// 组件中使用
import { useTranslations } from 'next-intl';
function Greeting({ name }: { name: string }) {
const t = useTranslations('common');
return (
<div>
<p>{t('hello', { name })}</p>
<p>{t('items', { count: 0 })}</p> {/* 没有项目 */}
<p>{t('items', { count: 5 })}</p> {/* 5个项目 */}
</div>
);
}
格式化与RTL
✅ tsx
// 日期/数字/货币格式化
import { useFormatter, useTranslations } from 'next-intl';
function ProductInfo({ product }: { product: Product }) {
const format = useFormatter();
const t = useTranslations('product');
return (
<div>
{/* 货币格式化 */}
<p className="price">
{format.number(product.price, { style: 'currency', currency: 'CNY' })}
</p>
{/* 日期格式化 */}
<p className="date">
{t('publishedAt', { date: new Date(product.createdAt) })}
</p>
{/* 相对时间 */}
<p className="relative">
{format.relativeTime(new Date(product.updatedAt))}
</p>
{/* 数字格式化 */}
<p className="sales">
已售 {format.number(product.sales, { notation: 'compact' })}
</p>
</div>
);
}
// RTL支持
function getDirection(locale: string): 'ltr' | 'rtl' {
const rtlLocales = ['ar', 'he', 'fa', 'ur'];
return rtlLocales.includes(locale) ? 'rtl' : 'ltr';
}
// RTL感知的CSS
// .flex-container {
// /* 自动适配RTL */
// display: flex;
// gap: var(--space-2);
/* /* LTR: margin-left: auto; RTL: margin-right: auto; */
/* margin-inline-start: auto; */
/* /* LTR: padding-left; RTL: padding-right */
/* padding-inline-start: var(--space-4); */
/* border-inline-start: 2px solid var(--color-accent); */
/* }
// 语言切换组件
import { useRouter, usePathname } from 'next/navigation';
import { useLocale } from 'next-intl';
function LanguageSwitcher() {
const router = useRouter();
const pathname = usePathname();
const currentLocale = useLocale();
const switchLocale = (newLocale: string) => {
router.replace(pathname.replace(\`/\${currentLocale}\`, \`/\${newLocale}\`));
};
return (
<select value={currentLocale} onChange={e => switchLocale(e.target.value)}>
<option value="zh">中文</option>
<option value="en">English</option>
<option value="ja">日本語</option>
</select>
);
}
🎯 练习任务
- 1 为Next.js应用添加中英日三语支持
- 2 实现复数规则和插值翻译
- 3 添加RTL语言支持(阿拉伯语)
- 4 创建翻译工作流:提取→翻译→合并
🏆 成就解锁
国际化工程师 — 掌握i18n全链路,让应用服务全球用户