agentsclimarketplace

I18n review

Skill kwhorne/elyra-skills/skills/i18n-review

51 production-grade Agent Skills for AI coding agents — full software lifecycle (idea → spec → build → review → ship → operate → maintain) plus Laravel/TALL/VILT/Filament stack workflows. Works with Elyra, Claude Code, Cursor, and more.

Install
npx -y skills add kwhorne/elyra-skills --skill i18n-review

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

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.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

6.6 KB, as published. Nobody here has run it

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 <locale>"

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: <scope>

**Framework:** <i18next / vue-i18n / Laravel / …>
**Locales in use:** <en, no, ar, …>

### 🔴 Blockers
- `path/file.tsx:42` — <issue>. **Impact:** <breaks for which locales/users>. **Fix:** <what>.

### 🟠 Major
- …

### 🟡 Minor
- …

### ✅ What's working
- <category> — <one sentence>

Severity rubric

TagMeaning
🔴 BlockerCannot be translated correctly (hardcoded strings, broken plurals, broken RTL layout)
🟠 MajorRenders incorrectly for many locales (date/number format, sorting, address forms)
🟡 MinorPolish (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

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.