Claude Code for Internationalization: i18n, l10n, and Multi-Language Apps — Claude Skills 360 Blog
Blog / Frontend / Claude Code for Internationalization: i18n, l10n, and Multi-Language Apps
Frontend

Claude Code for Internationalization: i18n, l10n, and Multi-Language Apps

Published: September 28, 2026
Read time: 8 min read
By: Claude Skills 360

Internationalization is more than translating strings. Pluralization rules differ across languages (English: 1 apple / 2 apples; Russian: 4 separate cases). Dates render differently. Numbers use different separators. Right-to-left languages flip layouts. Claude Code extracts strings from existing code, sets up ICU message format for complex cases, and generates locale-correct formatting.

Setup: next-intl for Next.js

CLAUDE.md for i18n Projects

## Internationalization Stack
- next-intl for Next.js App Router
- ICU message format (handles plurals, gender, select, dates)
- Message files: messages/{locale}.json
- Supported locales: en (default), es, fr, de, ja, ar (RTL)
- Locale routing: /en/..., /es/..., etc. (locale in URL path)
- Missing translations: fall back to English (never show translation keys)
// i18n.ts — locale detection and message loading
import { notFound } from 'next/navigation';
import { getRequestConfig } from 'next-intl/server';

const locales = ['en', 'es', 'fr', 'de', 'ja', 'ar'] as const;
export type Locale = (typeof locales)[number];

export default getRequestConfig(async ({ locale }) => {
  if (!locales.includes(locale as Locale)) notFound();

  return {
    messages: (await import(`./messages/${locale}.json`)).default,
  };
});
// messages/en.json
{
  "navigation": {
    "home": "Home",
    "products": "Products",
    "cart": "Cart ({count, number})"
  },
  "product": {
    "title": "{name}",
    "price": "{price, number, ::currency/USD}",
    "addToCart": "Add to Cart",
    "outOfStock": "Out of Stock",
    "stockRemaining": "{count, plural, =0 {Out of stock} one {Last {count} item} other {{count} items left}}"
  },
  "checkout": {
    "orderTotal": "Order Total: {total, number, ::currency/USD}",
    "itemCount": "You have {count, plural, one {# item} other {# items}} in your cart",
    "deliveryDate": "Estimated delivery: {date, date, long}"
  }
}
// messages/ar.json — Arabic (RTL, different plural rules: 6 forms!)
{
  "product": {
    "stockRemaining": "{count, plural, =0 {نفذ المخزون} one {آخر قطعة} two {آخر قطعتان} few {آخر {count} قطع} many {{count} قطعة} other {{count} قطعة}}"
  },
  "checkout": {
    "deliveryDate": "التسليم المتوقع: {date, date, long}"
  }
}

Components with Translation

Translate this product listing component.
Extract all text strings and replace with translation keys.
// app/[locale]/components/ProductCard.tsx
'use client';
import { useTranslations, useFormatter } from 'next-intl';
import type { Product } from '@/types';

// Before: hardcoded English
// function ProductCard({ product }) {
//   return (
//     <div>
//       <h2>{product.name}</h2>
//       <p>${product.price}</p>
//       {product.stock === 0 && <span>Out of stock</span>}
//     </div>
//   );
// }

// After: internationalized
export function ProductCard({ product }: { product: Product }) {
  const t = useTranslations('product');
  const format = useFormatter();
  
  return (
    <div>
      <h2>{product.name}</h2>
      
      {/* Currency: respects locale (€1.234,56 in Germany; $1,234.56 in US) */}
      <p>
        {format.number(product.priceCents / 100, {
          style: 'currency',
          currency: product.currency ?? 'USD',
        })}
      </p>
      
      {/* ICU select: handles all plural cases */}
      <span className={product.stock === 0 ? 'text-red' : 'text-green'}>
        {t('stockRemaining', { count: product.stock })}
      </span>
      
      {/* Formatted date */}
      <p>
        {t('deliveryDate', {
          date: new Date(product.estimatedDelivery),
        })}
      </p>
    </div>
  );
}

Extracting Strings from Existing Code

We have a large React app with hardcoded English strings everywhere.
Extract all visible text into translation keys.

Claude Code reads your components and generates the translation files:

// Input: existing component with hardcoded strings
function CheckoutSummary({ items, total }) {
  return (
    <div>
      <h2>Order Summary</h2>
      <p>You have {items.length} items in your cart</p>
      <p>Subtotal: ${(total / 100).toFixed(2)}</p>
      {items.length === 0 && <p>Your cart is empty. Start shopping!</p>}
      <button>Proceed to Payment</button>
    </div>
  );
}

// Output: internationalized version
function CheckoutSummary({ items, total, currency = 'USD' }) {
  const t = useTranslations('checkout');
  const format = useFormatter();
  
  return (
    <div>
      <h2>{t('orderSummaryTitle')}</h2>
      <p>{t('itemCount', { count: items.length })}</p>
      <p>{t('subtotal', { amount: format.number(total / 100, { style: 'currency', currency }) })}</p>
      {items.length === 0 && <p>{t('emptyCart')}</p>}
      <button>{t('proceedToPayment')}</button>
    </div>
  );
}

Generated strings to add to messages/en.json:

{
  "checkout": {
    "orderSummaryTitle": "Order Summary",
    "itemCount": "{count, plural, one {You have # item in your cart} other {You have # items in your cart}}",
    "subtotal": "Subtotal: {amount}",
    "emptyCart": "Your cart is empty. Start shopping!",
    "proceedToPayment": "Proceed to Payment"
  }
}

RTL Layout Support

We need to support Arabic and Hebrew — both RTL.
How do we handle layout mirroring without duplicating CSS?
// app/[locale]/layout.tsx — set dir attribute based on locale
import { getLocale } from 'next-intl/server';

const RTL_LOCALES = ['ar', 'he', 'fa', 'ur'];

export default async function RootLayout({ children }) {
  const locale = await getLocale();
  const dir = RTL_LOCALES.includes(locale) ? 'rtl' : 'ltr';
  
  return (
    <html lang={locale} dir={dir}>
      <body>{children}</body>
    </html>
  );
}
/* Use logical CSS properties — they flip automatically with dir="rtl" */

/* ❌ Physical properties — don't flip */
.nav { padding-left: 16px; text-align: left; }

/* ✅ Logical properties — flip with RTL automatically */
.nav { padding-inline-start: 16px; text-align: start; }

/* Icons that need to mirror (arrows, chevrons) */
[dir="rtl"] .arrow-icon {
  transform: scaleX(-1);
}

Number and Date Formatting

Show dates and numbers in the user's locale format.
// React hook: locale-aware formatting
import { useFormatter, useNow } from 'next-intl';

function OrderStatus({ order }) {
  const format = useFormatter();
  const now = useNow();
  
  return (
    <div>
      {/* Currency — locale-appropriate format */}
      <p>Total: {format.number(order.totalCents / 100, {
        style: 'currency',
        currency: order.currency,
      })}</p>
      {/* en-US: $1,234.56, de-DE: 1.234,56 €, ja-JP: ¥1,235 */}
      
      {/* Relative time */}
      <p>Ordered {format.relativeTime(new Date(order.createdAt), now)}</p>
      {/* en: "3 hours ago", es: "hace 3 horas", de: "vor 3 Stunden" */}
      
      {/* Absolute date */}
      <p>Delivered by {format.dateTime(new Date(order.deliveryDate), {
        weekday: 'long',
        month: 'long',
        day: 'numeric',
      })}</p>
      {/* en-US: "Wednesday, September 28", ar: "الأربعاء، ٢٨ سبتمبر" */}
      
      {/* List formatting */}
      <p>Ships to {format.list(order.availableRegions, { type: 'disjunction' })}</p>
      {/* en: "US, UK, or Canada", de: "USA, UK oder Kanada" */}
    </div>
  );
}

CI: Validate All Translations

How do I prevent missing translation keys from reaching production?
// scripts/validate-translations.ts
import { readdirSync, readFileSync } from 'fs';
import path from 'path';

const messagesDir = path.join(process.cwd(), 'messages');
const locales = readdirSync(messagesDir).map(f => f.replace('.json', ''));

// Load all translation files
const translations = Object.fromEntries(
  locales.map(locale => [
    locale,
    JSON.parse(readFileSync(path.join(messagesDir, `${locale}.json`), 'utf-8'))
  ])
);

// Get all keys from reference locale (English)
function getAllKeys(obj: object, prefix = ''): string[] {
  return Object.entries(obj).flatMap(([key, value]) => {
    const fullKey = prefix ? `${prefix}.${key}` : key;
    return typeof value === 'object' ? getAllKeys(value, fullKey) : [fullKey];
  });
}

const referenceKeys = new Set(getAllKeys(translations.en));

let hasErrors = false;

for (const locale of locales.filter(l => l !== 'en')) {
  const localeKeys = new Set(getAllKeys(translations[locale]));
  
  const missing = [...referenceKeys].filter(k => !localeKeys.has(k));
  const extra = [...localeKeys].filter(k => !referenceKeys.has(k));
  
  if (missing.length > 0) {
    console.error(`❌ ${locale}: Missing ${missing.length} keys:`, missing.slice(0, 5));
    hasErrors = true;
  }
  if (extra.length > 0) {
    console.warn(`⚠ ${locale}: ${extra.length} extra keys (not in en.json):`, extra.slice(0, 3));
  }
}

if (!hasErrors) {
  console.log('✅ All translations complete');
} else {
  process.exit(1);
}

For building React applications that use these i18n patterns, see the React guide. For Next.js App Router locale routing setup, the Next.js App Router guide covers the middleware configuration. The Claude Skills 360 bundle includes i18n skill sets for extraction, ICU message formatting, and RTL support. Start with the free tier to try translation extraction from existing components.

Keep Reading

Frontend

Claude Code for Chart.js Advanced: Custom Plugins and Mixed Charts

Advanced Chart.js patterns with Claude Code — chart.register() for tree-shaking, mixed chart types combining bar and line, custom plugin API with beforeDraw and afterDatasetsDraw hooks, ScriptableContext for computed colors, ChartDataLabels plugin for value labels, chartjs-plugin-zoom for pan and zoom, custom gradient fills via ctx.createLinearGradient, ChartJS annotation plugin for threshold lines, streaming data with chartjs-plugin-streaming, and react-chartjs-2 with useRef and chart instance.

6 min read Jun 27, 2027
Frontend

Claude Code for Nivo: Rich SVG and Canvas Charts

Build rich data visualizations with Nivo and Claude Code — ResponsiveLine and ResponsiveBar for adaptive charts, ResponsiveHeatMap for matrix data, ResponsiveTreeMap for hierarchal data, ResponsiveSunburst for nested proportions, ResponsiveChord for relationship diagrams, ResponsiveCalendar for activity heat maps, ResponsiveNetwork for force graphs, NivoTheme for consistent styling, tooltip customization with sliceTooltip, and motion config for spring animations.

6 min read Jun 26, 2027
Frontend

Claude Code for Victory Charts: React Native and Web Charts

Build cross-platform charts with Victory and Claude Code — VictoryChart, VictoryLine, VictoryBar, and VictoryScatter for web and React Native, VictoryPie for donut charts, VictoryArea for stacked areas, VictoryAxis for custom axes, VictoryTooltip and VictoryVoronoiContainer for hover tooltips, VictoryBrushContainer for range selection, VictoryZoomContainer for pan and zoom, VictoryLegend for series labels, custom theme with VictoryTheme, and VictoryStack for grouped bars.

6 min read Jun 25, 2027

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free