i18nlisted
Install: claude install-skill LDVerdier/rune
# Internationalization (i18n) guidelines
## Rule: No hardcoded user-facing strings
Every piece of text rendered in the UI must come from the i18n translation files. Never hardcode strings directly in JSX.
### Setup
The project uses **react-i18next** with bundled JSON translation files:
| File | Purpose |
|------|---------|
| `app/i18n/index.ts` | i18next initialization (auto-detects language from localStorage / navigator) |
| `app/i18n/locales/en.json` | English translations |
| `app/i18n/locales/fr.json` | French translations |
### How to add translated text
1. **Add keys** to both `en.json` and `fr.json` using nested objects grouped by feature/page:
```json
{
"myFeature": {
"title": "My Feature",
"description": "Some text"
}
}
```
2. **In React components**, use the `useTranslation` hook:
```tsx
import { useTranslation } from "react-i18next";
function MyComponent() {
const { t } = useTranslation();
return <h1>{t("myFeature.title")}</h1>;
}
```
3. **In `meta()` functions** (outside React context), use the i18n instance directly:
```tsx
import i18n from "~/i18n";
export function meta() {
const t = i18n.t;
return [{ title: t("myFeature.title") }];
}
```
4. **For dynamic values**, use i18next interpolation:
```tsx
t("myFeature.cost", { count: 5 })
// en.json: "Cost: {{count}} pts"
```
### Flat dot-notation keys for entity-like data
For **entity-like data** (characteristics, abilities, etc.) where each entry has a name plus associated metada