AgentStack
SKILL verified MIT Self-run

I18n

skill-pvnarp-agent-skills-i18n · by pvnarp

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.

No reviews yet
0 installs
7 views
0.0% view→install

Install

$ agentstack add skill-pvnarp-agent-skills-i18n

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of I18n? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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

{
  "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.

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

/* 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.

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

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.