# Std I18N

> Internationalization standards — locale files, key naming, pluralization, RTL, CSS logical properties across Rails/RN/Vite/Next. Use when adding user-facing strings or locales.

- Skill: `kaakati/std-i18n` (Agent Skill)
- Install (CLI): `npx skillmds@latest add kaakati/std-i18n`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kaakati/std-i18n/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: Kaakati (https://skillmd.com/u/kaakati)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/kaakati/std-i18n

---


# Internationalization (i18n) Conventions

## General Rules

- **Never hardcode user-facing strings** in source code. Always use translation keys.
- **Never concatenate** translated strings. Use interpolation: `t('greeting', name: user.name)`.
- **Fallback locale**: English (`en`) is the default fallback for all missing translations.
- **Locale detection**: Server reads `Accept-Language` header. Mobile reads device locale via `react-native-localize`.

## Rails i18n

### File Organization
- One YAML file per domain per locale: `backend/config/locales/{locale}/{domain}.{locale}.yml`
- Domains: `models`, `errors`, `notifications`, `mailers`, `api`
- Devise translations in `backend/config/locales/defaults/devise.{locale}.yml`

### Key Naming
- Dot-separated, hierarchical: `errors.not_found`, `activerecord.models.user`
- Use lazy lookup in controllers: `t('.success')` resolves from controller namespace
- Avoid deeply nested keys (max 4 levels)

### Pluralization
- Use Rails CLDR pluralization rules per locale
- Arabic requires all six plural forms: `zero`, `one`, `two`, `few`, `many`, `other`
- Always provide at minimum `one` and `other` forms

### Formatting
- Dates: `I18n.l(date, format: :short)` — never `strftime` directly
- Numbers: `number_to_currency`, `number_with_delimiter` — never manual formatting
- Define format patterns in locale YAML files, not in code

## React Native i18n

### File Organization
- JSON locale files in `mobile/src/i18n/locales/{locale}.json`
- Flat namespaced keys: `"orders.title"`, `"auth.login"`
- Initialization in `mobile/src/i18n/index.ts`

### Key Naming
- camelCase within namespaces: `"auth.forgotPassword"`
- Plurals use `_one` / `_other` suffixes (i18next convention)
- Context variants use `_male` / `_female` suffixes

### RTL Support
- Use `start`/`end` instead of `left`/`right` for layout properties
- Flip directional icons for RTL locales
- Test all screens in both LTR and RTL modes
- Use `I18nManager.isRTL` for conditional logic

## Web SPA i18n (Vite + react-i18next)

### Setup
- Use `react-i18next` (same library as React Native, without `react-native-localize`).
- Locale detection via `navigator.language` (browser API) instead of device locale.
- JSON locale files in `web/src/i18n/locales/{locale}.json` — same key format as React Native.
- Initialization in `web/src/i18n/index.ts`.

### Locale Detection
```typescript
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

i18n.use(LanguageDetector).use(initReactI18next).init({
  resources: { en: { translation: en }, ar: { translation: ar } },
  fallbackLng: 'en',
  detection: { order: ['navigator', 'htmlTag'] },
});
```

### Web RTL Support
- Use CSS logical properties (`margin-inline-start` instead of `margin-left`).
- Tailwind CSS `rtl:` variant for directional overrides.
- Set `dir="rtl"` on `<html>` element when locale is RTL.
- No `I18nManager` on web — use CSS-only RTL approach.

## Next.js i18n

### Server Component i18n
- Use `next-intl` or server-compatible i18next setup.
- Locale detection via `middleware.ts` reading `Accept-Language` header.
- Pass locale to Server Components via layout params or headers.
- Do NOT use `useTranslation` hook in Server Components — use a server-side `t()` function.

### Client Component i18n
- Use `useTranslation` from `react-i18next` in Client Components.
- Locale files bundled or loaded dynamically per route.

### Locale Routing
- Use Next.js middleware for locale-based routing (e.g., `/en/orders`, `/ar/orders`).
- Or use a single locale with `Accept-Language` header detection (simpler setup).

## CI Validation

- Run locale key parity check: all locale files must have the same keys as `en`
- Missing translations must fail CI in production builds
- Development builds show raw keys for easy identification of missing translations

