Claude Code Plugins

Community-maintained marketplace

Feedback

internationalization-localization

@chriscarterux/chris-claude-stack
1
0

This skill should be used when adding multi-language support or expanding to international markets - covers i18n setup with next-intl or react-i18next, translation management, RTL support, locale handling, currency formatting, and cultural adaptation for global applications.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name internationalization-localization
description This skill should be used when adding multi-language support or expanding to international markets - covers i18n setup with next-intl or react-i18next, translation management, RTL support, locale handling, currency formatting, and cultural adaptation for global applications.

Internationalization & Localization

Overview

Build applications that work globally. This skill teaches systematic approaches to multi-language support, cultural adaptation, and international expansion.

Core principle: i18n is infrastructure. Build it early or refactor extensively later.

When to Use

Use when:

  • Expanding to new markets
  • Adding multi-language support
  • Building for international audience from start
  • Localizing existing application
  • Handling currencies, dates, numbers across locales

i18n vs l10n

Internationalization (i18n):

  • Engineering work (code infrastructure)
  • Enable translation without code changes
  • Date/time/currency formatting
  • Character encoding (UTF-8)
  • One-time setup

Localization (l10n):

  • Content work (actual translations)
  • Cultural adaptation
  • Region-specific assets
  • Ongoing per-language

Next.js i18n Setup (Recommended)

Installation

npm install next-intl

Configuration

// i18n.config.ts
export const locales = ['en', 'es', 'fr', 'de', 'ja'] as const
export const defaultLocale = 'en' as const

export type Locale = (typeof locales)[number]

Directory Structure

messages/
  ├── en.json (English)
  ├── es.json (Spanish)
  ├── fr.json (French)
  ├── de.json (German)
  └── ja.json (Japanese)

Translation Files

// messages/en.json
{
  "common": {
    "welcome": "Welcome",
    "signIn": "Sign In",
    "signUp": "Sign Up"
  },
  "dashboard": {
    "title": "Dashboard",
    "greeting": "Hello, {name}!",
    "stats": {
      "users": "{count, plural, one {# user} other {# users}}"
    }
  }
}

// messages/es.json
{
  "common": {
    "welcome": "Bienvenido",
    "signIn": "Iniciar Sesión",
    "signUp": "Registrarse"
  },
  "dashboard": {
    "title": "Panel",
    "greeting": "¡Hola, {name}!",
    "stats": {
      "users": "{count, plural, one {# usuario} other {# usuarios}}"
    }
  }
}

Usage in Components

import {useTranslations} from 'next-intl'

export default function Dashboard() {
  const t = useTranslations('dashboard')

  return (
    <div>
      <h1>{t('title')}</h1>
      <p>{t('greeting', {name: 'John'})}</p>
      <p>{t('stats.users', {count: 1547})}</p>
    </div>
  )
}

Formatting Patterns

Dates

import {useFormatter} from 'next-intl'

const format = useFormatter()

// Automatic locale formatting
format.dateTime(new Date(), {
  year: 'numeric',
  month: 'long',
  day: 'numeric'
})

// en: January 15, 2025
// es: 15 de enero de 2025
// ja: 2025年1月15日

Numbers

// Numbers with separators
format.number(1234567)
// en: 1,234,567
// de: 1.234.567
// fr: 1 234 567

// Percentages
format.number(0.45, {style: 'percent'})
// 45%

// Compact notation
format.number(1200000, {notation: 'compact'})
// 1.2M

Currency

format.number(99.99, {
  style: 'currency',
  currency: 'USD'
})
// en-US: $99.99
// en-GB: US$99.99
// ja-JP: $99.99
// de-DE: 99,99 $

// Dynamic currency
format.number(price, {
  style: 'currency',
  currency: userCurrency // 'EUR', 'GBP', 'JPY', etc.
})

RTL (Right-to-Left) Support

RTL languages:

  • Arabic (ar)
  • Hebrew (he)
  • Persian (fa)
  • Urdu (ur)

CSS for RTL

/* Use logical properties */
.element {
  margin-inline-start: 16px; /* margin-left in LTR, margin-right in RTL */
  margin-inline-end: 8px;
  padding-inline: 12px;
}

/* Avoid directional properties */
.bad {
  margin-left: 16px; /* Doesn't flip in RTL */
}

Automatic RTL

// Set dir attribute based on locale
<html lang={locale} dir={isRTL(locale) ? 'rtl' : 'ltr'}>

function isRTL(locale: string) {
  return ['ar', 'he', 'fa', 'ur'].includes(locale)
}

Tailwind RTL

// Tailwind handles RTL automatically with logical properties
<div className="ms-4"> // margin-start (left in LTR, right in RTL)
<div className="me-4"> // margin-end
<div className="ps-4"> // padding-start
<div className="pe-4"> // padding-end

Pluralization

{
  "items": "{count, plural, =0 {No items} one {# item} other {# items}}"
}
t('items', {count: 0})  // "No items"
t('items', {count: 1})  // "1 item"
t('items', {count: 5})  // "5 items"

Translation Management

Workflow

  1. Extract strings

    • Move all user-facing text to translation files
    • Use t('key') everywhere
  2. Send for translation

    • Export JSON files
    • Hire native speakers (not Google Translate)
    • Professional services: Lokalise, Phrase, Crowdin
  3. Import translations

    • Add translated JSON files
    • Test in each language
  4. Continuous updates

    • New features → new strings
    • Track missing translations
    • Update regularly

Translation Keys Organization

{
  "pages": {
    "home": {
      "title": "Welcome",
      "hero": "Start building today"
    },
    "about": {
      "title": "About Us"
    }
  },
  "components": {
    "button": {
      "submit": "Submit",
      "cancel": "Cancel"
    }
  },
  "errors": {
    "required": "This field is required",
    "invalid_email": "Invalid email address"
  }
}

Locale Detection

Automatic Detection

// Browser language
const browserLocale = navigator.language // 'en-US', 'es-MX', etc.

// Extract language code
const language = browserLocale.split('-')[0] // 'en', 'es'

// Set initial locale
if (locales.includes(language as Locale)) {
  setLocale(language as Locale)
}

User Preference

// Save user choice
localStorage.setItem('locale', selectedLocale)

// Load on app start
const savedLocale = localStorage.getItem('locale') as Locale
if (savedLocale) {
  setLocale(savedLocale)
}

Language Switcher Component

import {useLocale} from 'next-intl'
import {useRouter, usePathname} from 'next/navigation'

function LanguageSwitcher() {
  const locale = useLocale()
  const router = useRouter()
  const pathname = usePathname()

  const changeLanguage = (newLocale: Locale) => {
    router.replace(pathname, {locale: newLocale})
  }

  return (
    <Select value={locale} onValueChange={changeLanguage}>
      <SelectTrigger>
        <SelectValue />
      </SelectTrigger>
      <SelectContent>
        <SelectItem value="en">English</SelectItem>
        <SelectItem value="es">Español</SelectItem>
        <SelectItem value="fr">Français</SelectItem>
        <SelectItem value="de">Deutsch</SelectItem>
      </SelectContent>
    </Select>
  )
}

Cultural Adaptation

Beyond Translation

Adapt for culture:

  • Images: Show diverse people, culturally appropriate imagery
  • Examples: Use local names, scenarios, references
  • Colors: Red = danger (Western), luck (China)
  • Symbols: Check marks, hand gestures vary
  • Formats: Dates, phone numbers, addresses differ

Market-Specific Content

Don't just translate:

  • Pricing (local currency, local pricing strategy)
  • Support (local hours, language)
  • Legal (local terms, privacy laws)
  • Payment methods (local popular methods)

Resources

  • next-intl: next-intl-docs.vercel.app
  • Format.js (Intl API): formatjs.io
  • Translation services: Lokalise, Phrase, Crowdin

i18n is investment in growth. Build it early, expand globally easily.