Die Oberfläche ist lokalisiert; Skill-Beschreibungen und -Dokumente werden per KI automatisch in Ihre Sprache übersetzt. Code, Befehle, Pfade und Fachbegriffe bleiben im Original, maßgeblich ist das Dokument des Autors.
Skills entdecken
Design und Kreativität · SKILL.md

frontend-internationalization

frontend-internationalization

Use this skill when the user says 'i18n', 'internationalization', 'localization', 'locale switching', 'RTL', 'right-to-left', 'i18next', 'react-intl', 'vue-i18n', 'translation', 'language selector', 'formatjs', 'ICU message format', 'pluralization', 'LTR', 'bidirectional text'. This skill enforces proper i18n library selection, locale switching patterns, RT…

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill-Dokumentation

GitHub

name: frontend-internationalization description: > Use this skill when the user says 'i18n', 'internationalization', 'localization', 'locale switching', 'RTL', 'right-to-left', 'i18next', 'react-intl', 'vue-i18n', 'translation', 'language selector', 'formatjs', 'ICU message format', 'pluralization', 'LTR', 'bidirectional text'. This skill enforces proper i18n library selection, locale switching patterns, RTL/LTR layout support, ICU message syntax, and translation file management. Works with any frontend framework (React, Vue, Angular, Svelte). Do NOT use for: backend i18n, database collation, or content translation workflows. version: "2.0.0" author: "j4flmao" license: "MIT" compatibility: claude-code: true cursor: true codex: true windsurf: true tags: [frontend, i18n, internationalization, universal]

Frontend Internationalization (i18n)

Purpose

Deliver multi-locale frontends with proper locale detection, resource loading, ICU message formatting, pluralization, date/number formatting, and full RTL layout support. The right library is chosen per framework. Translation files are kept separate from application logic.

Agent Protocol

Trigger

Exact phrases: "i18n", "internationalization", "localization", "locale switching", "RTL", "i18next", "react-intl", "vue-i18n", "translation", "language selector", "formatjs", "pluralization", "bidirectional text", "locale detection".

Input Context

  • Framework (React, Vue, Angular, Svelte, vanilla)
  • Locales required (e.g., en, fr, ar, he)
  • Existing i18n library or preference
  • SSR vs client-only rendering
  • Dynamic vs build-time locale loading
  • RTL support needed

Output Artifact

Complete i18n setup: library config, resource structure, locale switching mechanism, RTL styles, and usage patterns.

Response Format

## Strategy
<library, locale-loading, RTL-approach>

## Setup
<config, resource-imports>

## Usage
<components, hooks, format-patterns>

## RTL
<layout-switch, css-logical-properties>

—
Compression footer: frontend-i18n/v1 | locales: <count> | lib: <selected> | rtl: <bool>

No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output — why use many token when few do trick.

Completion Criteria

  • Locale resources loaded (dynamic or static) without affecting TTFB
  • Locale switching works without full page reload
  • ICU message format used for pluralization and interpolation
  • Dates, numbers, currencies formatted per locale
  • RTL layout switches correctly with CSS logical properties
  • Language direction attribute updates on the <html> element
  • Translation key fallback chain configured (locale → fallback locale)
  • SSR locale detection sends correct language to client

Max Response Length

4096 tokens

i18n Architecture / Decision Trees

Library Selection Decision Tree

Framework?
  |-- React -->
  |     SSR needed?
  |     |-- YES: next-i18next (Next.js) or react-i18next (any)
  |     |-- NO: react-i18next (full featured) or react-intl (ICU, smaller bundle)
  |
  |-- Vue -->
  |     |-- Vue 3: vue-i18n (official)
  |     |-- Nuxt: @nuxtjs/i18n
  |
  |-- Angular -->
  |     |-- @angular/localize (official, compile-time)
  |     |-- ngx-translate (runtime)
  |
  |-- Svelte -->
  |     |-- svelte-i18n or sveltekit-i18n
  |
  |-- Cross-framework / monorepo -->
        |-- i18next (framework-agnostic adapter)

Locale Loading Decision Tree

Number of locales?
  |-- 2-3 locales, small files (<50KB total) -->
  |     Bundle all translations? If yes: Include in main bundle (no loading delay)
  |     If no: Lazy load on locale switch
  |
  |-- 4-10 locales, moderate files -->
  |     Lazy-load per locale on demand. Split by namespace (common, auth, admin).
  |     Cache loaded resources in memory.
  |
  |-- 10+ locales -->
  |     Lazy-load per locale, per namespace. Only load active locale.
  |     Consider CDN-hosted translation files to reduce initial bundle.
  |
  |-- SSR application -->
        Load locale server-side, serialize to client via inline script.
        Hydrate client with server-loaded resources (no double fetch).

RTL Strategy Decision Tree

RTL support needed?
  |-- YES -->
  |     CSS approach?
  |     |-- Use CSS logical properties (inline-start/end) throughout
  |     |-- Set dir="rtl" on <html> element on locale change
  |     |-- Verify: all margin-left/right, padding-left/right, text-align left/right are replaced
  |     |
  |     Testing?
  |     |-- Visual: toggle locale to RTL, check every page
  |     |-- Automated: screenshot tests with RTL locale
  |
  |-- NO -->
        Ensure CSS logical properties are used anyway (future-proof).

Workflow

1. Library Selection

FrameworkLibraryNotes
Reactreact-i18next (i18next)Most mature, full feature set
Reactreact-intl (FormatJS)ICU message syntax, smaller bundle
Vue 2/3vue-i18nOfficial Vue i18n solution
Angular@angular/localizeBuilt-in Angular i18n
Sveltesvelte-i18nLightweight, reactive
Vanillai18nextFramework-agnostic

Prefer i18next for cross-framework projects or when full feature set is needed. Prefer FormatJS for React projects that need ICU message syntax and smaller bundle size.

2. Resource Structure

locales/
├── en/
│   ├── common.json
│   ├── auth.json
│   └── errors.json
├── fr/
│   ├── common.json
│   ├── auth.json
│   └── errors.json
└── ar/
    ├── common.json
    ├── auth.json
    └── errors.json

Namespaced translation files. Lazy-load namespaces on demand. Each namespace is a flat JSON file with dot-separated keys.

3. Locale Detection & Persistence

// Priority: URL > localStorage > cookie > navigator.language > fallback
import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'

i18n.use(LanguageDetector).init({
  detection: {
    order: ['localStorage', 'navigator', 'htmlTag'],
    caches: ['localStorage'],
  },
  fallbackLng: 'en',
  interpolation: { escapeValue: false },
})

4. Translation Usage

// Key-based translation with interpolation
t('auth.welcome', { name: 'Alice' })
// → "Welcome, Alice!"

// Pluralization
t('common.items', { count: items.length })
// count=0 → "No items"
// count=1 → "1 item"
// count=5 → "5 items"

// With context
t('common.status', { context: status })
// status='online' → "User is online"
// status='offline' → "User is offline"

5. ICU Message Format (FormatJS)

import { FormattedMessage, useIntl } from 'react-intl'

// Component
<FormattedMessage
  defaultMessage="{name} has {numPhotos, plural, =0 {no photos} =1 {one photo} other {# photos}}"
  values={{ name: 'Alice', numPhotos: 3 }}
/>

// Hook
const { formatMessage } = useIntl()
formatMessage({ defaultMessage: 'Hello {name}' }, { name: 'Bob' })

6. RTL Support

// Detect direction from locale
const isRTL = ['ar', 'he', 'fa', 'ur'].includes(locale)
document.documentElement.dir = isRTL ? 'rtl' : 'ltr'
document.documentElement.lang = locale

Prefer CSS logical properties over hardcoded left/right:

/* NOT THIS */
.sidebar { margin-left: 16px; text-align: left; }

/* THIS */
.sidebar { margin-inline-start: 16px; text-align: start; }

Logical properties automatically flip in RTL mode: margin-inline-start → right margin in RTL, padding-inline-end → left padding in RTL, border-inline-start → right border in RTL, inset-inline-start → right positioning in RTL.

7. Date & Number Formatting

// i18next
new Date().toLocaleDateString(locale, { dateStyle: 'long' })
new Intl.NumberFormat(locale, { style: 'currency', currency: 'EUR' }).format(amount)

// FormatJS
import { FormattedDate, FormattedNumber } from 'react-intl'

<FormattedDate value={date} dateStyle="long" />
<FormattedNumber value={price} style="currency" currency="USD" />
<FormattedNumber value={0.9} style="percent" />

8. SSR Locale Detection

// Next.js i18n (simpler approach — use middleware)
// Or accept-language header parsing
import acceptLanguage from 'accept-language'
acceptLanguage.languages(['en', 'fr', 'ar'])

export function getLocaleFromHeaders(request: Request): string {
  const acceptLang = request.headers.get('accept-language') || ''
  return acceptLanguage.parse(acceptLang)[0] || 'en'
}

9. Lazy Loading

i18n.use(initReactI18next).init({
  resources: {}, // start empty
  partialBundledLanguages: true,
})

// Load namespace on demand
async function loadNamespace(locale: string, ns: string) {
  const resources = await import(`./locales/${locale}/${ns}.json`)
  i18n.addResourceBundle(locale, ns, resources)
}

10. Testing with i18n

import { render } from '@testing-library/react'
import { I18nextProvider } from 'react-i18next'
import i18n from './i18n-test-config' // test-only config

function renderWithI18n(ui: React.ReactElement) {
  return render(<I18nextProvider i18n={i18n}>{ui}</I18nextProvider>)
}

11. Locale-Specific Number Formatting

const formatters = {
  currency: (locale: string, amount: number, currency: string) =>
    new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount),
  percent: (locale: string, value: number) =>
    new Intl.NumberFormat(locale, { style: 'percent', maximumFractionDigits: 1 }).format(value),
  unit: (locale: string, value: number, unit: string) =>
    new Intl.NumberFormat(locale, { style: 'unit', unit }).format(value),
}

// Usage
formatters.currency('de-DE', 19.99, 'EUR') // "19,99 €"
formatters.percent('en-US', 0.25)           // "25%"

Common Pitfalls

1. Hardcoded Strings in Components

// BAD -- hardcoded string, cannot be translated
<h2>Welcome back, {name}!</h2>

// GOOD -- translatable key
<h2>{t('auth.welcomeBack', { name })}</h2>

2. CSS left/right Instead of Logical Properties

Using left/right properties requires overriding every rule for RTL. CSS logical properties (inline-start/inline-end) flip automatically.

3. Not Persisting User Locale Choice

Detecting locale from navigator.language alone loses the user's choice after they switch. Always persist to localStorage or user preferences API.

4. Bundling All Locales

Importing all translation files in the main bundle increases initial JS size by 100KB+ per additional locale. Lazy-load by locale and namespace.

5. Missing Pluralization Rules

Some languages have complex plural rules (Arabic has 6 forms, Polish has 3). ICU handles this automatically — but only if you use proper plural syntax.

6. No Fallback Chain

When a translation key is missing in the target locale, the app should fall back to the default locale, then to the key itself. Without this, users see empty strings.

Compared With

Featurei18nextFormatJSvue-i18n@angular/localize
ICU message formatPluginNativeBuilt-inBuilt-in
PluralizationBuilt-inBuilt-inBuilt-inBuilt-in
Lazy loadingBuilt-inManualManualBuild-time
SSR supportYesYesYes (Nuxt)Build-time
TypeScript supportGoodGoodGoodBuilt-in
Bundle size~10KB~5KB~6KB0KB (compile-time)
Framework agnosticYesReact onlyVue onlyAngular only

Performance Considerations

Bundle Impact

  • Including i18next: ~10KB gzipped
  • Lazy-loaded locale resources per language: 2-20KB gzipped depending on app size
  • All 10 locales bundled eagerly: +100KB+ to initial JS
  • With lazy loading: only active locale loaded, ~2-20KB

SSR Serialization Cost

Serializing translation resources to the client adds ~5-50KB to the initial HTML payload. For large apps, consider streaming or loading only the current page's namespace.

RTL CSS Cost

CSS logical properties have negligible performance cost. Flipping layout on locale change triggers a full layout recalculation (style recalc + layout pass), typically taking 10-50ms.

Accessibility Considerations

  • dir and lang attributes on <html> are required for screen readers
  • RTL text must use the correct Unicode bidirectional characters
  • Icons with directional meaning (arrows, chevrons) should flip in RTL mode
  • Translation text may be longer in some languages — allow for text expansion (30-50% longer in German, Arabic)
  • Form labels and button text must be translated, not just content

Security Considerations

  • Never interpolate user input directly into translated strings (XSS risk)
  • i18next escapes values by default — do not disable escapeValue
  • Translation files from user-generated content (crowdsourced) must be sanitized
  • Avoid using dangerouslySetInnerHTML with translated content

Rules

  1. Never embed user-facing strings directly in components — always use translation keys.
  2. Translation keys follow a namespaced dot notation: namespace.key.subkey.
  3. Use ICU message format for pluralization, select, and ordinal rules.
  4. Never use CSS left/right properties — use CSS logical properties (inline-start/inline-end).
  5. HTML dir attribute and lang attribute are always in sync with current locale.
  6. Lazy-load translation files — never bundle all locales in the main chunk.
  7. Fallback chain always configured: target locale → fallback locale → key itself.
  8. Locale detection never relies solely on browser navigator.language — persist user choice.
  9. Numbers, dates, and currencies are formatted with Intl — never hardcoded.
  10. Translation interpolation escapes HTML by default to prevent XSS.

References

  • references/i18n-build-optimization.md — i18n Build Optimization
  • references/i18n-implementation.md — i18n Implementation
  • references/i18n-libraries.md — i18n Libraries
  • references/i18n-testing.md — i18n Testing
  • references/i18n-workflow.md — i18n Workflow
  • references/rtl-support.md — RTL Support

Handoff

No artifact produced unless requested. Next skill: frontend-accessibility — RTL a11y overlaps with i18n, pass locale direction config. Carry forward: locale list, RTL requirement, i18n library, lazy loading strategy.

Implementation Patterns

i18next Configuration

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';

i18n
  .use(Backend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    fallbackLng: 'en',
    debug: process.env.NODE_ENV === 'development',

    interpolation: {
      escapeValue: false,
    },

    detection: {
      order: ['localStorage', 'navigator', 'htmlTag', 'cookie'],
      caches: ['localStorage'],
    },

    backend: {
      loadPath: '/locales/{{lng}}/{{ns}}.json',
    },

    ns: ['common', 'auth', 'errors'],
    defaultNS: 'common',

    returnObjects: true,
  });

export default i18n;

Translation Component

import { useTranslation, Trans } from 'react-i18next';

function WelcomeBanner({ userName, itemCount, lastLogin }: WelcomeProps) {
  const { t, i18n } = useTranslation('common');

  const dateFormatter = new Intl.DateTimeFormat(i18n.language, {
    dateStyle: 'long',
  });

  return (
    <div dir={i18n.dir()}>
      <h1>{t('welcome.title', { name: userName })}</h1>

      {/* Simple interpolation */}
      <p>{t('welcome.item_count', { count: itemCount })}</p>

      {/* Pluralization (ICU format) */}
      <p>{t('welcome.items', { count: itemCount })}</p>

      {/* Complex translation with embedded HTML */}
      <Trans i18nKey="welcome.last_login">
        Last login: <strong>{{ date: dateFormatter.format(lastLogin) }}</strong>
      </Trans>
    </div>
  );
}

Architecture Decision Trees

i18n Library Selection

What framework and requirements?
├── React
│   ├── Need full feature set → i18next + react-i18next
│   ├── Simple, lightweight → react-intl (FormatJS)
│   └── Type-safe translations → typesafe-i18n
│
├── Vue
│   ├── Full featured → vue-i18n
│   └── Lightweight → @intlify/vue-i18n-core
│
├── Angular
│   └── @angular/localize (built-in)
│
└── No framework / vanilla
    └── i18next (framework-agnostic core)

Anti-Patterns

Anti-PatternWhy It FailsCorrect Approach
Hardcoding strings in componentsCan't translate without code changesAlways use translation keys
Bundling all locales in main chunkBloated initial bundleLazy-load translation files per locale
Single locale detection methodFails for users in different regionChain: persisted preference > browser > fallback
No ICU message formatPluralization breaks for many languagesUse ICU format for plural/select/ordinal
Ignoring text direction in CSSLTR layouts break for RTL languagesUse CSS logical properties (inline-start/end)

Performance Optimization

  • Lazy-loaded locale chunks: Split translation files per locale and namespace. Load only the user's current locale. Prefetch next likely locale on hover or idle time.
  • ICU message format compilation: Precompile ICU messages at build time. Avoids runtime parsing overhead. Reduces translation evaluation time by 5x.

Production Considerations

Deployment Checklist

  • Configuration validated against schema before startup
  • Health check endpoints registered and monitored
  • Graceful shutdown with draining period (30s timeout)
  • Resource limits configured (CPU, memory, file descriptors)
  • Log level set appropriate for environment
  • Metrics endpoint secured and exposed
  • Rate limiting configured per-tier
  • TLS certificates valid and auto-renewing
  • Database migrations run as separate deployment step
  • Feature flags ready for gradual rollout

Monitoring and Alerting

MetricThresholdSeverityAction
Error rate> 1% over 5minCriticalPage on-call
p99 latency> 2s over 5minWarningInvestigate
Throughput drop> 50% over 1minCriticalCheck upstream
Queue depth> 1000 over 1minWarningScale consumers
Disk usage> 85%WarningClean or expand
Memory usage> 90% heapCriticalRestart or scale
Gespeicherte Skills