i18n — Internationalization Skill
Arguments
$0 - What to translate (e.g., "DRep dashboard labels", "new filter buttons")
Quick Reference
Locales: en, de, fr, es, pt, ja, zh — files at src/messages/{locale}.json
Library: next-intl — useTranslations("namespace") in components, typed labels object for lib/ utilities
CRITICAL: All 7 files must stay in sync. Missing keys cause runtime errors.
Procedure
- Grep for the insertion point in
en.json (find neighboring keys)
- Read just those lines (offset/limit) across all 7 files
- Edit all 7 files — add key after the neighboring key
- Wire up in component:
const t = useTranslations("namespace"); t("key")
Namespaces
| Namespace |
Purpose |
common |
Shared UI: loading, retry, search, showMore |
meta |
Page titles/descriptions |
landing |
Landing page |
dashboard |
Dashboard page |
stats |
Stat cards |
status |
Status labels: Active, Ratified, Enacted |
actionTypes |
NoConfidence, UpdateCommittee, etc. |
filters |
Filter UI |
table |
Table headers |
voting |
drep, spo, cc, yes, no, abstain, threshold |
charts |
Dashboard chart titles |
sort |
Sort controls |
proposal |
Proposal detail page |
export |
Export file labels |
drep |
DRep dashboard |
drep.profile |
DRep profile page |
tabs |
Tab labels |
expiry |
Proposal expiry |
errors |
Error states |
download |
Download dropdown |
accessibility |
Screen reader labels |
Max one nesting level (e.g., charts.proposalStatus.title).
Project Conventions — What Stays English
- Landing page filter dropdown labels
- Action type names and status values on proposal cards
- Machine-readable values (API constants, JSON export keys, tx hashes)
- Technical terms: DRep, SPO, ADA, CC — English in all locales
Translation Guidelines
- Natural fluent translations, not word-for-word
- CJK (ja, zh): no spaces, appropriate particles
- Portuguese: European (pt-PT)
- Interpolation: ICU syntax
"Show {count} more" — ensure {count} in all locales
- CSV export: prepend UTF-8 BOM (
"\uFEFF") for Excel compatibility
Pattern: Labels for lib/ Utilities
Functions in lib/ can't use hooks. Pass a typed labels object from the component:
// lib/export.ts
export interface ExportLabels { noRationale: string; voteYes: string; }
export function exportToCSV(data: Data[], labels: ExportLabels) { ... }
// Component
const labels: ExportLabels = useMemo(() => ({
noRationale: t("noRationale"), voteYes: t("voteYes"),
}), [t]);
Key Rules
- Always use original English values for styling/logic (
getVoteBadgeClasses(vote.vote)), translated values only for display
useLocale() for date/number formatting: new Date(ts).toLocaleDateString(locale, {...})
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: i18n3description: Add or update translations across all 7 locale files and wire them into React components or pure utility functions. Use when this capability is needed.4---56# i18n — Internationalization Skill78## Arguments9- `$0` - What to translate (e.g., "DRep dashboard labels", "new filter buttons")1011## Quick Reference1213**Locales**: `en`, `de`, `fr`, `es`, `pt`, `ja`, `zh` — files at `src/messages/{locale}.json`14**Library**: `next-intl` — `useTranslations("namespace")` in components, typed labels object for `lib/` utilities15**CRITICAL**: All 7 files must stay in sync. Missing keys cause runtime errors.1617## Procedure18191. **Grep** for the insertion point in `en.json` (find neighboring keys)202. **Read** just those lines (offset/limit) across all 7 files213. **Edit** all 7 files — add key after the neighboring key224. **Wire up** in component: `const t = useTranslations("namespace"); t("key")`2324## Namespaces2526| Namespace | Purpose |27|-----------|---------|28| `common` | Shared UI: `loading`, `retry`, `search`, `showMore` |29| `meta` | Page titles/descriptions |30| `landing` | Landing page |31| `dashboard` | Dashboard page |32| `stats` | Stat cards |33| `status` | Status labels: `Active`, `Ratified`, `Enacted` |34| `actionTypes` | `NoConfidence`, `UpdateCommittee`, etc. |35| `filters` | Filter UI |36| `table` | Table headers |37| `voting` | `drep`, `spo`, `cc`, `yes`, `no`, `abstain`, `threshold` |38| `charts` | Dashboard chart titles |39| `sort` | Sort controls |40| `proposal` | Proposal detail page |41| `export` | Export file labels |42| `drep` | DRep dashboard |43| `drep.profile` | DRep profile page |44| `tabs` | Tab labels |45| `expiry` | Proposal expiry |46| `errors` | Error states |47| `download` | Download dropdown |48| `accessibility` | Screen reader labels |4950Max one nesting level (e.g., `charts.proposalStatus.title`).5152## Project Conventions — What Stays English5354- Landing page filter dropdown labels55- Action type names and status values on proposal cards56- Machine-readable values (API constants, JSON export keys, tx hashes)57- Technical terms: DRep, SPO, ADA, CC — English in all locales5859## Translation Guidelines6061- Natural fluent translations, not word-for-word62- CJK (ja, zh): no spaces, appropriate particles63- Portuguese: European (pt-PT)64- Interpolation: ICU syntax `"Show {count} more"` — ensure `{count}` in all locales65- CSV export: prepend UTF-8 BOM (`"\uFEFF"`) for Excel compatibility6667## Pattern: Labels for lib/ Utilities6869Functions in `lib/` can't use hooks. Pass a typed labels object from the component:7071```typescript72// lib/export.ts73export interface ExportLabels { noRationale: string; voteYes: string; }74export function exportToCSV(data: Data[], labels: ExportLabels) { ... }7576// Component77const labels: ExportLabels = useMemo(() => ({78 noRationale: t("noRationale"), voteYes: t("voteYes"),79}), [t]);80```8182## Key Rules8384- Always use original English values for styling/logic (`getVoteBadgeClasses(vote.vote)`), translated values only for display85- `useLocale()` for date/number formatting: `new Date(ts).toLocaleDateString(locale, {...})`8687---88> Converted and distributed by [TomeVault](https://tomevault.io/claim/nomos-guild) — claim your Tome and manage your conversions.89<!-- tomevault:4.0:skill_md:2026-04-13 -->