---
name: i18n
source: botcore
description: >
  Internationalization and localization best practices.
  Use when adding multi-language support, handling RTL layouts, or externalizing strings.
version: "1.0.0"
triggers:
  - i18n
  - internationalization
  - l10n
  - localization
  - translations
  - RTL
  - locale
  - language
---

# Internationalization (i18n)

Best practices for building apps that work across languages and regions.

## When to Use

- Adding multi-language support
- Preparing for international markets
- Handling RTL (right-to-left) layouts
- Formatting dates, numbers, currency
- Externalizing hardcoded strings

## Core Concepts

| Term | Meaning |
|------|---------|
| **i18n** | Internationalization — making code locale-aware |
| **l10n** | Localization — adapting for specific locale |
| **Locale** | Language + region (e.g., `en-US`, `fr-CA`) |
| **RTL** | Right-to-left scripts (Arabic, Hebrew) |

## String Externalization

```typescript
// ❌ Bad: Hardcoded strings
const message = "Welcome back, " + userName + "!";

// ✅ Good: Externalized with interpolation
const message = t('welcome_back', { name: userName });
// en.json: { "welcome_back": "Welcome back, {{name}}!" }
// es.json: { "welcome_back": "¡Bienvenido de nuevo, {{name}}!" }
```

## Translation File Structure

```
locales/
├── en/
│   ├── common.json
│   ├── errors.json
│   └── dashboard.json
├── es/
│   ├── common.json
│   ├── errors.json
│   └── dashboard.json
└── ar/
    └── ...
```

## Pluralization

```json
// en.json
{
  "items_count": {
    "zero": "No items",
    "one": "{{count}} item",
    "other": "{{count}} items"
  }
}

// ru.json (has more plural forms)
{
  "items_count": {
    "one": "{{count}} элемент",
    "few": "{{count}} элемента",
    "many": "{{count}} элементов",
    "other": "{{count}} элемента"
  }
}
```

## Date/Time Formatting

```typescript
// ✅ Use Intl API
const formatter = new Intl.DateTimeFormat('de-DE', {
  dateStyle: 'long',
  timeStyle: 'short',
});
formatter.format(new Date()); // "30. Januar 2026 um 08:45"
```

## Number/Currency Formatting

```typescript
// Numbers
new Intl.NumberFormat('de-DE').format(1234567.89);
// "1.234.567,89"

// Currency
new Intl.NumberFormat('ja-JP', {
  style: 'currency',
  currency: 'JPY',
}).format(1000);
// "￥1,000"
```

## RTL Support

```css
/* Use logical properties */
.card {
  /* ❌ Physical */
  margin-left: 1rem;
  padding-right: 2rem;
  
  /* ✅ Logical */
  margin-inline-start: 1rem;
  padding-inline-end: 2rem;
}

/* Set direction */
html[dir="rtl"] {
  direction: rtl;
}
```

## Popular Libraries

| Library | Platform | Notes |
|---------|----------|-------|
| `i18next` | JS/React | Feature-rich, plugins |
| `react-intl` | React | FormatJS ecosystem |
| `vue-i18n` | Vue | Official Vue solution |
| `gettext` | Python | Classic, widespread |
| `fluent` | Mozilla | Advanced grammar |

## Translation Workflow

1. **Extract** — Scan code for translatable strings
2. **Translate** — Send to translators (Crowdin, Lokalise)
3. **Review** — Native speaker QA
4. **Import** — Pull translations into codebase
5. **Test** — Verify in-context

## Anti-Patterns

| Avoid | Instead |
|-------|---------|
| String concatenation | Template interpolation |
| Hardcoded formats | `Intl` APIs |
| Assuming text length | Flexible layouts |
| Images with text | Separate text layer |
| `en` as fallback for `en-GB` | Explicit fallback chain |

## Checklist

- [ ] No hardcoded user-facing strings
- [ ] Pluralization handled correctly
- [ ] Dates/numbers use `Intl` or library
- [ ] RTL layout tested (if applicable)
- [ ] Translation files complete (no missing keys)
- [ ] Context provided for translators
- [ ] Pseudo-localization tested (catches layout issues)
