localization-engineer
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLocalization Engineer Skill
本地化工程师技能
I help you build multilingual applications with proper internationalization (i18n) and localization (l10n) support.
我可以帮助你构建具备完善国际化(i18n)和本地化(l10n)支持的多语言应用。
What I Do
我的服务内容
Internationalization:
- Multi-language text content
- Date/time formatting
- Number and currency formatting
- Right-to-left (RTL) support
Localization:
- Translation management
- Language detection
- Language switching
- Locale-specific content
国际化:
- 多语言文本内容
- 日期/时间格式化
- 数字和货币格式化
- 从右到左(RTL)语言支持
本地化:
- 翻译管理
- 语言检测
- 语言切换
- 区域特定内容
Next.js Internationalization
Next.js 国际化
Setup with next-intl
使用 next-intl 进行设置
bash
npm install next-intltypescript
// i18n/request.ts
import { getRequestConfig } from 'next-intl/server'
export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`../messages/${locale}.json`)).default
}))typescript
// middleware.ts
import createMiddleware from 'next-intl/middleware'
export default createMiddleware({
locales: ['en', 'es', 'fr', 'de', 'ja'],
defaultLocale: 'en'
})
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)']
}bash
npm install next-intltypescript
// i18n/request.ts
import { getRequestConfig } from 'next-intl/server'
export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`../messages/${locale}.json`)).default
}))typescript
// middleware.ts
import createMiddleware from 'next-intl/middleware'
export default createMiddleware({
locales: ['en', 'es', 'fr', 'de', 'ja'],
defaultLocale: 'en'
})
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)']
}Translation Files
翻译文件
json
// messages/en.json
{
"common": {
"welcome": "Welcome",
"loading": "Loading...",
"error": "Something went wrong"
},
"navigation": {
"home": "Home",
"about": "About",
"contact": "Contact"
},
"auth": {
"login": "Log in",
"logout": "Log out",
"signUp": "Sign up",
"emailPlaceholder": "Enter your email",
"passwordPlaceholder": "Enter your password"
}
}json
// messages/es.json
{
"common": {
"welcome": "Bienvenido",
"loading": "Cargando...",
"error": "Algo salió mal"
},
"navigation": {
"home": "Inicio",
"about": "Acerca de",
"contact": "Contacto"
},
"auth": {
"login": "Iniciar sesión",
"logout": "Cerrar sesión",
"signUp": "Registrarse",
"emailPlaceholder": "Ingrese su correo electrónico",
"passwordPlaceholder": "Ingrese su contraseña"
}
}json
// messages/en.json
{
"common": {
"welcome": "Welcome",
"loading": "Loading...",
"error": "Something went wrong"
},
"navigation": {
"home": "Home",
"about": "About",
"contact": "Contact"
},
"auth": {
"login": "Log in",
"logout": "Log out",
"signUp": "Sign up",
"emailPlaceholder": "Enter your email",
"passwordPlaceholder": "Enter your password"
}
}json
// messages/es.json
{
"common": {
"welcome": "Bienvenido",
"loading": "Cargando...",
"error": "Algo salió mal"
},
"navigation": {
"home": "Inicio",
"about": "Acerca de",
"contact": "Contacto"
},
"auth": {
"login": "Iniciar sesión",
"logout": "Cerrar sesión",
"signUp": "Registrarse",
"emailPlaceholder": "Ingrese su correo electrónico",
"passwordPlaceholder": "Ingrese su contraseña"
}
}Using Translations
使用翻译内容
Client Component
客户端组件
typescript
'use client'
import { useTranslations } from 'next-intl'
export function LoginForm() {
const t = useTranslations('auth')
return (
<form>
<input
type="email"
placeholder={t('emailPlaceholder')}
/>
<input
type="password"
placeholder={t('passwordPlaceholder')}
/>
<button>{t('login')}</button>
</form>
)
}typescript
'use client'
import { useTranslations } from 'next-intl'
export function LoginForm() {
const t = useTranslations('auth')
return (
<form>
<input
type="email"
placeholder={t('emailPlaceholder')}
/>
<input
type="password"
placeholder={t('passwordPlaceholder')}
/>
<button>{t('login')}</button>
</form>
)
}Server Component
服务端组件
typescript
import { useTranslations } from 'next-intl'
export default function HomePage() {
const t = useTranslations('common')
return (
<div>
<h1>{t('welcome')}</h1>
</div>
)
}typescript
import { useTranslations } from 'next-intl'
export default function HomePage() {
const t = useTranslations('common')
return (
<div>
<h1>{t('welcome')}</h1>
</div>
)
}Language Switcher
语言切换器
typescript
'use client'
import { useLocale } from 'next-intl'
import { useRouter, usePathname } from 'next/navigation'
const languages = [
{ code: 'en', name: 'English', flag: '🇺🇸' },
{ code: 'es', name: 'Español', flag: '🇪🇸' },
{ code: 'fr', name: 'Français', flag: '🇫🇷' },
{ code: 'de', name: 'Deutsch', flag: '🇩🇪' },
{ code: 'ja', name: '日本語', flag: '🇯🇵' }
]
export function LanguageSwitcher() {
const locale = useLocale()
const router = useRouter()
const pathname = usePathname()
const switchLanguage = (newLocale: string) => {
// Remove current locale from pathname
const pathWithoutLocale = pathname.replace(`/${locale}`, '')
// Navigate to new locale
router.push(`/${newLocale}${pathWithoutLocale}`)
}
return (
<select
value={locale}
onChange={(e) => switchLanguage(e.target.value)}
className="px-4 py-2 border rounded"
>
{languages.map((lang) => (
<option key={lang.code} value={lang.code}>
{lang.flag} {lang.name}
</option>
))}
</select>
)
}typescript
'use client'
import { useLocale } from 'next-intl'
import { useRouter, usePathname } from 'next/navigation'
const languages = [
{ code: 'en', name: 'English', flag: '🇺🇸' },
{ code: 'es', name: 'Español', flag: '🇪🇸' },
{ code: 'fr', name: 'Français', flag: '🇫🇷' },
{ code: 'de', name: 'Deutsch', flag: '🇩🇪' },
{ code: 'ja', name: '日本語', flag: '🇯🇵' }
]
export function LanguageSwitcher() {
const locale = useLocale()
const router = useRouter()
const pathname = usePathname()
const switchLanguage = (newLocale: string) => {
// Remove current locale from pathname
const pathWithoutLocale = pathname.replace(`/${locale}`, '')
// Navigate to new locale
router.push(`/${newLocale}${pathWithoutLocale}`)
}
return (
<select
value={locale}
onChange={(e) => switchLanguage(e.target.value)}
className="px-4 py-2 border rounded"
>
{languages.map((lang) => (
<option key={lang.code} value={lang.code}>
{lang.flag} {lang.name}
</option>
))}
</select>
)
}Date and Time Formatting
日期和时间格式化
typescript
'use client'
import { useFormatter } from 'next-intl'
export function FormattedDate({ date }: { date: Date }) {
const format = useFormatter()
return (
<div>
{/* Full date */}
<p>{format.dateTime(date, { dateStyle: 'full' })}</p>
{/* Short date */}
<p>{format.dateTime(date, { dateStyle: 'short' })}</p>
{/* Custom format */}
<p>{format.dateTime(date, {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: 'numeric'
})}</p>
{/* Relative time */}
<p>{format.relativeTime(date)}</p>
</div>
)
}
// Examples:
// en: "Monday, October 22, 2025"
// es: "lunes, 22 de octubre de 2025"
// ja: "2025年10月22日月曜日"typescript
'use client'
import { useFormatter } from 'next-intl'
export function FormattedDate({ date }: { date: Date }) {
const format = useFormatter()
return (
<div>
{/* Full date */}
<p>{format.dateTime(date, { dateStyle: 'full' })}</p>
{/* Short date */}
<p>{format.dateTime(date, { dateStyle: 'short' })}</p>
{/* Custom format */}
<p>{format.dateTime(date, {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: 'numeric'
})}</p>
{/* Relative time */}
<p>{format.relativeTime(date)}</p>
</div>
)
}
// Examples:
// en: "Monday, October 22, 2025"
// es: "lunes, 22 de octubre de 2025"
// ja: "2025年10月22日月曜日"Number and Currency Formatting
数字和货币格式化
typescript
'use client'
import { useFormatter } from 'next-intl'
export function FormattedNumber({ value }: { value: number }) {
const format = useFormatter()
return (
<div>
{/* Number */}
<p>{format.number(value)}</p>
{/* Currency */}
<p>{format.number(value, { style: 'currency', currency: 'USD' })}</p>
{/* Percentage */}
<p>{format.number(value / 100, { style: 'percent' })}</p>
{/* Compact notation */}
<p>{format.number(value, { notation: 'compact' })}</p>
</div>
)
}
// Examples:
// en: "1,234.56" "$1,234.56" "12%" "1.2K"
// de: "1.234,56" "1.234,56 $" "12 %" "1200"
// ja: "1,234.56" "$1,234.56" "12%" "1.2千"typescript
'use client'
import { useFormatter } from 'next-intl'
export function FormattedNumber({ value }: { value: number }) {
const format = useFormatter()
return (
<div>
{/* Number */}
<p>{format.number(value)}</p>
{/* Currency */}
<p>{format.number(value, { style: 'currency', currency: 'USD' })}</p>
{/* Percentage */}
<p>{format.number(value / 100, { style: 'percent' })}</p>
{/* Compact notation */}
<p>{format.number(value, { notation: 'compact' })}</p>
</div>
)
}
// Examples:
// en: "1,234.56" "$1,234.56" "12%" "1.2K"
// de: "1.234,56" "1.234,56 $" "12 %" "1200"
// ja: "1,234.56" "$1,234.56" "12%" "1.2千"Pluralization
复数处理
json
// messages/en.json
{
"items": {
"count": "{count, plural, =0 {No items} one {# item} other {# items}}"
}
}typescript
'use client'
import { useTranslations } from 'next-intl'
export function ItemCounter({ count }: { count: number }) {
const t = useTranslations('items')
return <p>{t('count', { count })}</p>
}
// count = 0: "No items"
// count = 1: "1 item"
// count = 5: "5 items"json
// messages/en.json
{
"items": {
"count": "{count, plural, =0 {No items} one {# item} other {# items}}"
}
}typescript
'use client'
import { useTranslations } from 'next-intl'
export function ItemCounter({ count }: { count: number }) {
const t = useTranslations('items')
return <p>{t('count', { count })}</p>
}
// count = 0: "No items"
// count = 1: "1 item"
// count = 5: "5 items"RTL (Right-to-Left) Support
RTL(从右到左)语言支持
typescript
// app/[locale]/layout.tsx
import { useLocale } from 'next-intl'
const rtlLanguages = ['ar', 'he', 'fa']
export default function LocaleLayout({ children }) {
const locale = useLocale()
const isRTL = rtlLanguages.includes(locale)
return (
<html lang={locale} dir={isRTL ? 'rtl' : 'ltr'}>
<body>{children}</body>
</html>
)
}RTL CSS:
css
/* Automatically flips for RTL */
.container {
margin-inline-start: 1rem; /* Use logical properties */
padding-inline-end: 1rem;
}
/* Manual RTL handling */
[dir='rtl'] .menu {
left: auto;
right: 0;
}typescript
// app/[locale]/layout.tsx
import { useLocale } from 'next-intl'
const rtlLanguages = ['ar', 'he', 'fa']
export default function LocaleLayout({ children }) {
const locale = useLocale()
const isRTL = rtlLanguages.includes(locale)
return (
<html lang={locale} dir={isRTL ? 'rtl' : 'ltr'}>
<body>{children}</body>
</html>
)
}RTL CSS:
css
/* Automatically flips for RTL */
.container {
margin-inline-start: 1rem; /* Use logical properties */
padding-inline-end: 1rem;
}
/* Manual RTL handling */
[dir='rtl'] .menu {
left: auto;
right: 0;
}Language Detection
语言检测
typescript
// lib/detect-locale.ts
export function detectUserLocale(): string {
// 1. Check URL parameter
const urlParams = new URLSearchParams(window.location.search)
const urlLocale = urlParams.get('lang')
if (urlLocale) return urlLocale
// 2. Check localStorage
const savedLocale = localStorage.getItem('preferredLocale')
if (savedLocale) return savedLocale
// 3. Check browser language
const browserLocale = navigator.language.split('-')[0]
return browserLocale
// 4. Default
return 'en'
}typescript
// lib/detect-locale.ts
export function detectUserLocale(): string {
// 1. Check URL parameter
const urlParams = new URLSearchParams(window.location.search)
const urlLocale = urlParams.get('lang')
if (urlLocale) return urlLocale
// 2. Check localStorage
const savedLocale = localStorage.getItem('preferredLocale')
if (savedLocale) return savedLocale
// 3. Check browser language
const browserLocale = navigator.language.split('-')[0]
return browserLocale
// 4. Default
return 'en'
}Translation with Variables
带变量的翻译
json
// messages/en.json
{
"welcome": "Welcome, {name}!",
"itemsInCart": "You have {count} {count, plural, one {item} other {items}} in your cart",
"priceDisplay": "Price: {price, number, ::currency/USD}"
}typescript
'use client'
import { useTranslations } from 'next-intl'
export function Greeting({ userName }: { userName: string }) {
const t = useTranslations()
return (
<div>
<h1>{t('welcome', { name: userName })}</h1>
<p>{t('itemsInCart', { count: 3 })}</p>
<p>{t('priceDisplay', { price: 49.99 })}</p>
</div>
)
}
// Output:
// "Welcome, John!"
// "You have 3 items in your cart"
// "Price: $49.99"json
// messages/en.json
{
"welcome": "Welcome, {name}!",
"itemsInCart": "You have {count} {count, plural, one {item} other {items}} in your cart",
"priceDisplay": "Price: {price, number, ::currency/USD}"
}typescript
'use client'
import { useTranslations } from 'next-intl'
export function Greeting({ userName }: { userName: string }) {
const t = useTranslations()
return (
<div>
<h1>{t('welcome', { name: userName })}</h1>
<p>{t('itemsInCart', { count: 3 })}</p>
<p>{t('priceDisplay', { price: 49.99 })}</p>
</div>
)
}
// Output:
// "Welcome, John!"
// "You have 3 items in your cart"
// "Price: $49.99"Locale-Specific Content
区域特定内容
typescript
// app/[locale]/page.tsx
import { useLocale } from 'next-intl'
export default function HomePage() {
const locale = useLocale()
const content = {
en: {
hero: 'Build amazing apps',
description: 'The best platform for developers'
},
es: {
hero: 'Crea aplicaciones increíbles',
description: 'La mejor plataforma para desarrolladores'
},
ja: {
hero: '素晴らしいアプリを作成',
description: '開発者のための最高のプラットフォーム'
}
}
return (
<div>
<h1>{content[locale].hero}</h1>
<p>{content[locale].description}</p>
</div>
)
}typescript
// app/[locale]/page.tsx
import { useLocale } from 'next-intl'
export default function HomePage() {
const locale = useLocale()
const content = {
en: {
hero: 'Build amazing apps',
description: 'The best platform for developers'
},
es: {
hero: 'Crea aplicaciones increíbles',
description: 'La mejor plataforma para desarrolladores'
},
ja: {
hero: '素晴らしいアプリを作成',
description: '開発者のための最高のプラットフォーム'
}
}
return (
<div>
<h1>{content[locale].hero}</h1>
<p>{content[locale].description}</p>
</div>
)
}Translation Management
翻译管理
Using Translation Service (Lokalise, Crowdin)
使用翻译服务(Lokalise、Crowdin)
typescript
// scripts/sync-translations.ts
async function syncTranslations() {
// Download translations from service
const response = await fetch('https://api.lokalise.com/api2/projects/PROJECT_ID/files/download', {
headers: {
'X-Api-Token': process.env.LOKALISE_API_KEY!
}
})
const data = await response.json()
// Save to messages folder
await fs.writeFile('./messages/en.json', JSON.stringify(data.en, null, 2))
console.log('Translations synced!')
}typescript
// scripts/sync-translations.ts
async function syncTranslations() {
// Download translations from service
const response = await fetch('https://api.lokalise.com/api2/projects/PROJECT_ID/files/download', {
headers: {
'X-Api-Token': process.env.LOKALISE_API_KEY!
}
})
const data = await response.json()
// Save to messages folder
await fs.writeFile('./messages/en.json', JSON.stringify(data.en, null, 2))
console.log('Translations synced!')
}Missing Translation Handling
缺失翻译的处理
typescript
// i18n/request.ts
import { getRequestConfig } from 'next-intl/server'
export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`../messages/${locale}.json`)).default,
onError: error => {
console.error('Translation error:', error)
},
getMessageFallback: ({ namespace, key, error }) => {
return `${namespace}.${key}` // Show key if translation missing
}
}))typescript
// i18n/request.ts
import { getRequestConfig } from 'next-intl/server'
export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`../messages/${locale}.json`)).default,
onError: error => {
console.error('Translation error:', error)
},
getMessageFallback: ({ namespace, key, error }) => {
return `${namespace}.${key}` // Show key if translation missing
}
}))SEO for Multilingual Sites
多语言站点的SEO优化
typescript
// app/[locale]/layout.tsx
import { useLocale } from 'next-intl'
export async function generateMetadata({ params: { locale } }) {
const t = await useTranslations('metadata')
return {
title: t('title'),
description: t('description'),
alternates: {
canonical: `/${locale}`,
languages: {
en: '/en',
es: '/es',
fr: '/fr',
de: '/de',
ja: '/ja'
}
}
}
}HTML Output:
html
<link rel="canonical" href="https://example.com/en" />
<link rel="alternate" hreflang="en" href="https://example.com/en" />
<link rel="alternate" hreflang="es" href="https://example.com/es" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr" />typescript
// app/[locale]/layout.tsx
import { useLocale } from 'next-intl'
export async function generateMetadata({ params: { locale } }) {
const t = await useTranslations('metadata')
return {
title: t('title'),
description: t('description'),
alternates: {
canonical: `/${locale}`,
languages: {
en: '/en',
es: '/es',
fr: '/fr',
de: '/de',
ja: '/ja'
}
}
}
}HTML输出:
html
<link rel="canonical" href="https://example.com/en" />
<link rel="alternate" hreflang="en" href="https://example.com/en" />
<link rel="alternate" hreflang="es" href="https://example.com/es" />
<link rel="alternate" hreflang="fr" href="https://example.com/fr" />When to Use Me
何时选择我
Perfect for:
- Building multilingual applications
- International product launches
- Global SaaS platforms
- E-commerce in multiple countries
- Content management systems
I'll help you:
- Set up i18n infrastructure
- Manage translations
- Format dates, numbers, currencies
- Handle RTL languages
- Optimize for SEO
适用场景:
- 构建多语言应用
- 国际化产品发布
- 全球SaaS平台
- 多国家电商平台
- 内容管理系统
我可以帮你:
- 搭建i18n基础设施
- 管理翻译内容
- 格式化日期、数字、货币
- 处理RTL语言
- 优化SEO
What I'll Create
我能创建的内容
🌍 Multi-Language Support
📅 Date/Time Formatting
💰 Currency Formatting
🔄 Language Switching
📝 Translation Management
🌐 RTL SupportLet's make your app globally accessible!
🌍 多语言支持
📅 日期/时间格式化
💰 货币格式化
🔄 语言切换
📝 翻译管理
🌐 RTL语言支持让我们一起让你的应用走向全球!