# I18n

> Guides internationalization (i18n) and localization (l10n) implementation. Covers string extraction, locale handling, date/number/currency formatting, RTL support, and translation workflows. Use when adding multi-language support or reviewing i18n implementation.

- **Type:** Skill
- **Install:** `agentstack add skill-pvnarp-agent-skills-i18n`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [pvnarp](https://agentstack.voostack.com/s/pvnarp)
- **Installs:** 0
- **Category:** [Cloud & Infrastructure](https://agentstack.voostack.com/c/cloud-infrastructure)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [pvnarp](https://github.com/pvnarp)
- **Source:** https://github.com/pvnarp/agent-skills/tree/main/skills/i18n

## Install

```sh
agentstack add skill-pvnarp-agent-skills-i18n
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Internationalization (i18n)

i18n is architecture. l10n is content. Get the architecture right first - adding languages later becomes trivial.

## Core Principles

1. **No hardcoded user-facing strings.** Every string the user sees comes from a translation file.
2. **Locale-aware formatting.** Dates, numbers, currency, plurals - never format these yourself.
3. **Design for expansion.** German text is ~30% longer than English. UI must accommodate.
4. **Separate translation from code.** Translators edit translation files, not source code.

## String Externalization

### Before (hardcoded)
```
"3 items in your cart"
"Welcome back, John"
"January 15, 2024"
```

### After (externalized)
```
t('cart.item_count', { count: 3 })       → "3 items in your cart" / "3 Artikel in Ihrem Warenkorb"
t('greeting.welcome_back', { name })     → "Welcome back, John" / "Willkommen zurück, John"
formatDate(date, { locale })             → "January 15, 2024" / "15. Januar 2024"
```

### Translation File Structure
```json
{
  "cart": {
    "item_count": "{count, plural, one {# item in your cart} other {# items in your cart}}",
    "empty": "Your cart is empty",
    "checkout": "Proceed to checkout"
  },
  "greeting": {
    "welcome_back": "Welcome back, {name}"
  }
}
```

**Rules:**
- Keys are hierarchical and descriptive (`cart.item_count`, not `str_47`)
- Keep translations close to where they're used (per-page or per-component bundles for large apps)
- English is the source of truth - other locales are translations of it
- Include context comments for translators when meaning is ambiguous

## Pluralization

Different languages have different plural rules. English has 2 forms (1, other). Arabic has 6. Russian has 3. Never do this:

```
// WRONG
count === 1 ? "1 item" : `${count} items`
```

Use ICU MessageFormat or your framework's plural support:

```
"{count, plural, one {# item} other {# items}}"
```

| Language | Plural Forms | Example |
|----------|-------------|---------|
| English | one, other | 1 item, 2 items |
| French | one, other | (but 0 is "one") |
| Russian | one, few, many, other | 1 файл, 2 файла, 5 файлов |
| Arabic | zero, one, two, few, many, other | 6 forms |
| Japanese | other | (no plural) |

## Date, Number, and Currency Formatting

**Never format manually.** Use `Intl` (JS), `NumberFormat` / `DateFormat` (JVM), or equivalent.

```javascript
// Dates
new Intl.DateTimeFormat('de-DE', { dateStyle: 'long' }).format(date)
// → "15. Januar 2024"

// 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"
```

**Gotchas:**
- Decimal separator: `.` (US) vs `,` (EU) vs `٫` (Arabic)
- Date order: MM/DD/YYYY (US) vs DD/MM/YYYY (EU) vs YYYY/MM/DD (ISO/Asia)
- Currency symbol position: $100 (US) vs 100€ (EU) vs 100 ₽ (Russia)
- Week starts on: Sunday (US) vs Monday (EU/ISO)

## RTL (Right-to-Left) Support

Required for Arabic, Hebrew, Persian, Urdu.

### CSS
```css
/* Use logical properties instead of physical */
margin-inline-start: 1rem;   /* NOT margin-left */
padding-inline-end: 0.5rem;  /* NOT padding-right */
border-inline-start: 1px solid; /* NOT border-left */
text-align: start;            /* NOT text-align: left */

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

### Layout
- Flexbox and Grid with `start`/`end` handle RTL automatically
- Icons with directional meaning (arrows, progress) must be mirrored
- Phone numbers, code blocks, and URLs stay LTR even in RTL context

## Implementation Checklist

### Phase 1: Architecture
- [ ] i18n library/framework integrated
- [ ] Locale detection (browser, user preference, URL)
- [ ] Translation file structure defined
- [ ] All user-facing strings externalized (no hardcoded strings)

### Phase 2: Formatting
- [ ] Dates use locale-aware formatting
- [ ] Numbers use locale-aware formatting
- [ ] Currency uses locale-aware formatting with correct currency code
- [ ] Pluralization uses ICU/CLDR rules (not `if count === 1`)

### Phase 3: Layout
- [ ] UI handles 30%+ text expansion without breaking
- [ ] CSS uses logical properties (`start`/`end` not `left`/`right`)
- [ ] RTL layout tested (if supporting RTL languages)
- [ ] Fonts support target language character sets

### Phase 4: Workflow
- [ ] Translation file format works with translator tools
- [ ] New strings flagged for translation in code review
- [ ] Missing translations fall back to default language gracefully
- [ ] Translation coverage tracked per locale

## Common Mistakes

| Mistake | Problem | Fix |
|---------|---------|-----|
| Concatenating translated strings | "Hello " + name + "!" breaks in other languages (word order differs) | Use interpolation: `t('hello', { name })` |
| Hardcoded date format | "01/02/2024" - is that Jan 2 or Feb 1? | Use `Intl.DateTimeFormat` |
| Assuming text length | German/Finnish can be 30-50% longer | Design flexible layouts |
| Translating dev-facing text | Error codes, log messages | Only translate user-facing text |
| Image with embedded text | Can't translate | Use text overlays or separate images per locale |

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [pvnarp](https://github.com/pvnarp)
- **Source:** [pvnarp/agent-skills](https://github.com/pvnarp/agent-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-pvnarp-agent-skills-i18n
- Seller: https://agentstack.voostack.com/s/pvnarp
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
