AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

I18n Review

skill-kwhorne-elyra-skills-i18n-review · by kwhorne

Audit code and UI for internationalization correctness - string extraction, pluralization, dates/numbers/currencies, RTL support, and locale-aware sorting. Use when the user asks for an i18n review, localization audit, or wants to make an app translation-ready.

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

Install

$ agentstack add skill-kwhorne-elyra-skills-i18n-review

✓ 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.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-kwhorne-elyra-skills-i18n-review)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of I18n Review? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

i18n Review

The goal: a translator can ship a new locale without touching code, and the result is correct for native speakers, not "English with words swapped."

When to use

  • "Review X for i18n / localization"
  • "Audit translation readiness"
  • "Make this app translatable"
  • "Add support for "

Procedure

  1. Identify the frameworki18next, react-intl, vue-i18n, Laravel __(), gettext, Rails I18n, custom? Match existing conventions.
  2. Walk the checklist below
  3. Group findings by severity

Checklist

String extraction

  • [ ] No hardcoded user-facing strings in templates, components, or controllers
  • [ ] Strings live in translation files (en.json, messages.po, lang/en/…)
  • [ ] Translation keys are stable identifiers, not the English text itself (auth.login.title not "Sign in")
  • [ ] Keys aren't constructed dynamically (t('errors.' + code) defeats static extraction — use a switch with explicit keys instead)
  • [ ] No concatenated sentences: t('hello') + ' ' + name → use interpolation: t('hello_name', {name})
  • [ ] No HTML embedded in translatable strings — use placeholders or rich-text helpers
  • [ ] Plurals use the framework's plural helper, not if (n === 1)

Plurals

Plural rules vary wildly. English has 2 forms; Polish has 4; Arabic has 6. Don't fake it:

// ❌ Don't
const msg = count === 1 ? '1 item' : `${count} items`;

// ✅ Do
t('item_count', { count })
// en.json: { "item_count_one": "{{count}} item", "item_count_other": "{{count}} items" }
// pl.json: { "item_count_one": "...", "item_count_few": "...", "item_count_many": "...", "item_count_other": "..." }

Dates, numbers, currencies

  • [ ] No hardcoded date formats (YYYY-MM-DD) for display — use Intl.DateTimeFormat or framework helper
  • [ ] Numbers formatted with locale separators (1,234.56 vs 1.234,56 vs 1 234,56)
  • [ ] Currencies formatted with Intl.NumberFormat(locale, {style:'currency', currency}), never by string concat ('$' + amount)
  • [ ] Currency code stored, locale-formatted on display
  • [ ] Time zones explicit — store UTC, display in user's TZ
  • [ ] Week start day not hardcoded (Sunday vs Monday vs Saturday varies)

Text direction (RTL)

  • [ ] Layout uses logical properties: margin-inline-start not margin-left, padding-inline-end not padding-right
  • [ ] dir="rtl" flips the layout correctly (test with Arabic, Hebrew, or ?dir=rtl)
  • [ ] Icons that imply direction (arrows, back/forward) flip in RTL
  • [ ] Numbers and code blocks stay LTR even in RTL contexts
  • [ ] No text-align: left when you mean "start" — use text-align: start

Sorting & collation

  • [ ] Lists sorted with Intl.Collator(locale), not byte-wise / Array.sort()
  • [ ] Case-insensitive comparisons use localeCompare with proper options
  • [ ] Search/filter is accent-insensitive where appropriate (sensitivity: 'base')

Input

  • [ ] Name fields accept unicode (no [a-zA-Z]+ validation)
  • [ ] Email validation allows internationalized domains
  • [ ] Phone number format not hardcoded to one country (use libphonenumber or similar)
  • [ ] Address forms don't assume US-style state/zip
  • [ ] Long words / long translations don't break layout (German is famously verbose)

Tooling

  • [ ] Linter / extractor rule prevents new hardcoded strings (eslint-plugin-i18next, i18n-ally, phpcs rules, …)
  • [ ] CI fails if translations are missing keys (or warns with a baseline)
  • [ ] Locale fallback chain is explicit (fr-CA → fr → en)
  • [ ] Language switching doesn't require full reload (where the framework supports it)

Quick greps

# Hardcoded English strings in templates (heuristic)
grep -RInE '>[A-Z][a-z]+ [a-z]+' --include='*.{vue,jsx,tsx,blade.php,erb}' . | head

# Concatenated translations
grep -RInE "t\([^)]+\)\s*\+|i18n\.[a-z]+\([^)]+\)\s*\+" --include='*.{js,ts,jsx,tsx,vue}' .

# Hardcoded currency symbols
grep -RInE "['\"\`]\\\$\{|\\\$[0-9]|'\\\$'" --include='*.{js,ts,jsx,tsx,vue,blade.php}' .

# Physical CSS properties that should be logical
grep -RInE 'margin-(left|right)|padding-(left|right)|text-align:\s*(left|right)' \
  --include='*.{css,scss,vue,jsx,tsx}' .

# Date format strings that look hardcoded
grep -RInE "format\(['\"](YYYY|DD|MM)" --include='*.{js,ts,jsx,tsx,vue}' .

Output format

## i18n review: 

**Framework:** 
**Locales in use:** 

### 🔴 Blockers
- `path/file.tsx:42` — . **Impact:** . **Fix:** .

### 🟠 Major
- …

### 🟡 Minor
- …

### ✅ What's working
-  — 

Severity rubric

| Tag | Meaning | |---|---| | 🔴 Blocker | Cannot be translated correctly (hardcoded strings, broken plurals, broken RTL layout) | | 🟠 Major | Renders incorrectly for many locales (date/number format, sorting, address forms) | | 🟡 Minor | Polish (long-text overflow, missing locale fallback) |

Anti-patterns

  • t('You have ' + count + ' messages') — broken in every language with grammatical agreement
  • ❌ Translating button text but baking in width: 80px (German "Einstellungen" doesn't fit)
  • ❌ Storing formatted currency in DB ("$10.00")
  • ❌ Sorting names with Array.sort() instead of Intl.Collator
  • ❌ Right-aligned designs that "just work" in LTR
  • ❌ Assuming names have a first and a last name with a space between
  • ❌ Validating names with [a-zA-Z\s]+
  • ❌ Mirror-flipping all icons in RTL (don't flip clocks, logos, media controls)

Useful references

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.