Install
$ agentstack add skill-pvnarp-agent-skills-i18n ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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.
About
Internationalization (i18n)
i18n is architecture. l10n is content. Get the architecture right first - adding languages later becomes trivial.
Core Principles
- No hardcoded user-facing strings. Every string the user sees comes from a translation file.
- Locale-aware formatting. Dates, numbers, currency, plurals - never format these yourself.
- Design for expansion. German text is ~30% longer than English. UI must accommodate.
- 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, notstr_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/endhandle 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/endnotleft/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
- Source: pvnarp/agent-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.