
Localization Engineer
- 150 installs
- 33 repo stars
- Updated December 25, 2025
- daffy0208/ai-dev-standards
Add i18n, RTL layouts, locale-aware formatting, and translation workflows so products ship in multiple languages without breaking UX or SEO.
About
Engineers localization for web and mobile apps: ICU messages, pluralization, RTL support, locale detection, translation management, and CI checks for missing keys. Covers hreflang considerations, pseudolocale testing, and handoff patterns so teams ship multilingual SaaS and ecommerce without layout regressions or stale strings.
- i18n key extraction
- RTL and layout fixes
- Locale routing
- Translation workflow design
- Date, number, currency formatting
Localization Engineer by the numbers
- 150 all-time installs (skills.sh)
- Ranked #955 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
npx skills add https://github.com/daffy0208/ai-dev-standards --skill localization-engineerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 150 |
|---|---|
| repo stars | ★ 33 |
| Last updated | December 25, 2025 |
| Repository | daffy0208/ai-dev-standards ↗ |
What it does
Add i18n, RTL layouts, locale-aware formatting, and translation workflows so products ship in multiple languages without breaking UX or SEO.
Files
Localization Engineer Skill
I help you build multilingual applications with proper internationalization (i18n) and localization (l10n) support.
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
Next.js Internationalization
Setup with next-intl
npm install next-intl// i18n/request.ts
import { getRequestConfig } from 'next-intl/server'
export default getRequestConfig(async ({ locale }) => ({
messages: (await import(`../messages/${locale}.json`)).default
}))// 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
// 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"
}
}// 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
'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
import { useTranslations } from 'next-intl'
export default function HomePage() {
const t = useTranslations('common')
return (
<div>
<h1>{t('welcome')}</h1>
</div>
)
}---
Language Switcher
'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
'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
'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
// messages/en.json
{
"items": {
"count": "{count, plural, =0 {No items} one {# item} other {# items}}"
}
}'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
// 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:
/* 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
// 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
// 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}"
}'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
// 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)
// 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
// 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
// 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:
<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
What I'll Create
🌍 Multi-Language Support
📅 Date/Time Formatting
💰 Currency Formatting
🔄 Language Switching
📝 Translation Management
🌐 RTL SupportLet's make your app globally accessible!
name: localization-engineer
kind: skill
description: Expert in internationalization (i18n), multi-language support, and localization
preconditions:
- check: project_initialized
description: Project environment is set up
required: true
effects:
- builds_multilingual
domains: &id001
- ai
- rag
- api
- frontend
- backend
- security
- testing
- product
- design
- data
cost: medium
latency: medium
risk_level: low
side_effects:
- modifies_files
- creates_artifacts
idempotent: false
success_signal: localization-engineer capability successfully applied
failure_signals:
- Prerequisites not met
- Configuration error
compatibility:
requires: []
conflicts_with: []
composes_with: []
enables: []
observability:
logs:
- Applying localization-engineer...
- localization-engineer completed
metrics:
- execution_time_ms
- success_rate
metadata:
version: 1.0.0
created_at: '2025-10-30'
tags: *id001
examples: []
Localization Engineer Skill
Expert in internationalization (i18n), multi-language support, and localization.
Quick Start
# Activate skill
claude-code --skill localization-engineerWhat This Skill Does
- 🌍 Implements multi-language support (next-intl)
- 📅 Formats dates/times per locale
- 💰 Formats numbers and currencies
- 🔄 Builds language switchers
- 📝 Manages translations
- 🌐 Supports RTL languages
Common Tasks
Setup i18n
"Set up internationalization for a Next.js app with English, Spanish, and Japanese"Add Translations
"Create translation files for the navigation menu and auth pages"Language Switcher
"Build a language switcher dropdown with flags"Format Dates
"Format this date according to the user's locale"Technologies
- next-intl - Next.js i18n library
- Intl API - Native formatting
- TypeScript - Type-safe translations
- React - UI components
Example Output
// Multi-language component
const t = useTranslations('auth')
<button>{t('login')}</button>
// en: "Log in"
// es: "Iniciar sesión"
// ja: "ログイン"Related Skills
copywriter- Writing translationsseo-optimizer- Multilingual SEOaccessibility-auditor- Accessible i18n
Learn More
See SKILL.md for comprehensive localization patterns.