---
name: i18n-l10n
description: Internationalization and localization for the boilerplate monorepo. Covers backend (i18next), frontend (next-intl), and mobile (react-i18next) with shared types.
---

# Internationalization & Localization (i18n/L10n)

Complete i18n toolkit for the monorepo boilerplate - backend, frontend, and mobile.

## Overview

**Default:** Türkçe (tr) | **Supported:** tr, en

| Layer        | Library                      | Locale Detection                   | Storage                |
| ------------ | ---------------------------- | ---------------------------------- | ---------------------- |
| **Backend**  | i18next + i18next-fs-backend | Accept-Language header             | In-memory (JSON files) |
| **Frontend** | next-intl                    | URL segment (`/tr/...`, `/en/...`) | Cookie + URL           |
| **Mobile**   | react-i18next                | Device locale + AsyncStorage       | AsyncStorage           |

## Shared Types

All i18n types are centralized in `@boilerplate/types`:

```typescript
import {
  SupportedLocale, // 'tr' | 'en'
  SUPPORTED_LOCALES, // ['tr', 'en'] as const
  DEFAULT_LOCALE, // 'tr'
  FALLBACK_LOCALE, // 'tr'
  LOCALE_NAMES, // { tr: 'Turkish', en: 'English' }
  LOCALE_NATIVE_NAMES, // { tr: 'Türkçe', en: 'English' }
  ErrorCode, // API error codes
  LocalizedApiError, // Localized API error response type
  isLocaleSupported, // Type guard function
} from '@boilerplate/types';
```

## File Structure

```
packages/
├── shared/
│   ├── types/src/i18n.ts                    # Shared type definitions
│   ├── backend/src/i18n/
│   │   ├── index.ts                         # Exports
│   │   ├── i18n.service.ts                  # Singleton service
│   │   ├── i18n.middleware.ts               # Express middleware
│   │   └── locales/
│   │       ├── tr/
│   │       │   ├── common.json
│   │       │   ├── errors.json
│   │       │   └── validation.json
│   │       └── en/
│   │           ├── common.json
│   │           ├── errors.json
│   │           └── validation.json
│   └── mobile/src/lib/
│       ├── i18n.ts                          # Mobile i18n config
│       └── locales/
│           ├── tr.json
│           └── en.json
├── frontend/nextjs-template/
│   ├── src/i18n/
│   │   ├── config.ts                        # Locale config
│   │   ├── request.ts                       # Server request config
│   │   └── navigation.ts                    # Locale-aware navigation
│   ├── src/messages/
│   │   ├── tr.json
│   │   └── en.json
│   ├── src/middleware.ts                    # Locale routing
│   └── src/app/[locale]/                    # Locale-aware routes
└── mobile/expo-template/
    └── src/components/LanguageSwitcher.tsx  # Mobile language picker
```

---

## Backend Usage

### Initialize i18n Service

```typescript
// app.ts
import { i18nService, createI18nMiddleware } from '@boilerplate/shared-backend';

export async function createApp() {
  const app = express();

  // Initialize i18n
  await i18nService.init({
    debug: process.env.NODE_ENV === 'development',
    localesPath: path.join(__dirname, '../src/i18n/locales'),
  });

  // Add middleware
  app.use(cookieParser());
  app.use(createI18nMiddleware());

  return app;
}
```

### Translate in Controllers/Services

```typescript
// Request object has t() function attached by middleware
app.get('/api/greeting', (req, res) => {
  // req.locale - detected locale ('tr' | 'en')
  // req.localeSource - how locale was detected ('header' | 'query' | 'cookie' | 'default')
  // req.t() - translation function

  res.json({
    message: req.t('common.welcome'),
    greeting: req.t('common.hello', { name: 'Atakan' }),
  });
});
```

### Localized API Errors

```typescript
import { ApiError } from '@boilerplate/shared-backend';

// Throw error with error code for localized message
throw ApiError.notFoundWithCode('USER_NOT_FOUND', { resource: 'User' });

// Error middleware returns localized response:
// {
//   "success": false,
//   "error": {
//     "message": "Kullanıcı bulunamadı",  // Localized message
//     "messageKey": "USER_NOT_FOUND",      // Key for client override
//     "statusCode": 404,
//     "status": "Not Found"
//   }
// }
```

---

## Frontend Usage (Next.js)

### In Server Components

```typescript
import { useTranslations } from 'next-intl';

export default function HomePage() {
  const t = useTranslations('common');

  return (
    <div>
      <h1>{t('welcome')}</h1>
      <p>{t('greeting', { name: 'Atakan' })}</p>
    </div>
  );
}
```

### In Client Components

```typescript
'use client';

import { useTranslations, useLocale } from 'next-intl';

export function ClientComponent() {
  const t = useTranslations('auth');
  const locale = useLocale();

  return (
    <button>{t('login')}</button>
  );
}
```

### Locale-Aware Navigation

```typescript
// Use locale-aware Link from navigation.ts
import { Link, useRouter, usePathname, redirect } from '@/i18n/navigation';

export function Navigation() {
  const router = useRouter();
  const pathname = usePathname();

  return (
    <nav>
      <Link href="/dashboard">{t('dashboard')}</Link>
      <Link href="/settings">{t('settings')}</Link>
    </nav>
  );
}
```

### Language Switcher

```typescript
'use client';

import { useLocale } from 'next-intl';
import { usePathname, useRouter } from '@/i18n/navigation';
import { SUPPORTED_LOCALES, LOCALE_NATIVE_NAMES, type SupportedLocale } from '@boilerplate/types';

export function LocaleSwitcher() {
  const locale = useLocale();
  const pathname = usePathname();
  const router = useRouter();

  const switchLocale = (newLocale: SupportedLocale) => {
    router.replace(pathname, { locale: newLocale });
  };

  return (
    <select value={locale} onChange={(e) => switchLocale(e.target.value as SupportedLocale)}>
      {SUPPORTED_LOCALES.map((loc) => (
        <option key={loc} value={loc}>{LOCALE_NATIVE_NAMES[loc]}</option>
      ))}
    </select>
  );
}
```

---

## Mobile Usage (React Native)

### Initialize on App Start

```typescript
// app/_layout.tsx
import { initI18n } from '@boilerplate/shared-mobile';

export default function RootLayout() {
  const [isI18nReady, setIsI18nReady] = useState(false);

  useEffect(() => {
    initI18n()
      .then(() => setIsI18nReady(true))
      .catch((error) => {
        console.error('i18n init failed:', error);
        setIsI18nReady(true); // Continue with defaults
      });
  }, []);

  if (!isI18nReady) {
    return <LoadingScreen />;
  }

  return <Stack />;
}
```

### Translate in Components

```typescript
import { useTranslation } from 'react-i18next';

export function MyScreen() {
  const { t } = useTranslation();

  return (
    <View>
      <Text>{t('common.welcome')}</Text>
      <Text>{t('auth.login')}</Text>
    </View>
  );
}
```

### Change Language

```typescript
import { changeLanguage, getCurrentLanguage } from '@boilerplate/shared-mobile';
import { type SupportedLocale } from '@boilerplate/types';

// Get current language
const current = getCurrentLanguage(); // 'tr' | 'en'

// Change language (persists to AsyncStorage)
await changeLanguage('en');
```

---

## Adding a New Language

### Step 1: Update Types

```typescript
// packages/shared/types/src/i18n.ts
export type SupportedLocale = 'tr' | 'en' | 'de'; // Add new locale

export const SUPPORTED_LOCALES: readonly SupportedLocale[] = ['tr', 'en', 'de'] as const;

export const LOCALE_NAMES: Record<SupportedLocale, string> = {
  tr: 'Turkish',
  en: 'English',
  de: 'German', // Add name
};

export const LOCALE_NATIVE_NAMES: Record<SupportedLocale, string> = {
  tr: 'Türkçe',
  en: 'English',
  de: 'Deutsch', // Add native name
};
```

### Step 2: Add Backend Translations

Create `packages/shared/backend/src/i18n/locales/de/` directory with:

- `common.json`
- `errors.json`
- `validation.json`

### Step 3: Add Frontend Translations

Create `packages/frontend/nextjs-template/src/messages/de.json`

### Step 4: Add Mobile Translations

Create `packages/shared/mobile/src/lib/locales/de.json`

Then update `packages/shared/mobile/src/lib/i18n.ts`:

```typescript
import de from './locales/de.json';

const resources = {
  tr: { translation: tr },
  en: { translation: en },
  de: { translation: de }, // Add new locale
};
```

### Step 5: Update Frontend Middleware

```typescript
// packages/frontend/nextjs-template/src/i18n/config.ts
export const locales = ['tr', 'en', 'de'] as const;
```

### Step 6: Rebuild Types

```bash
pnpm --filter @boilerplate/types build
```

---

## Translation Key Structure

Minimal boilerplate keys - projects add their own:

```json
{
  "common": {
    "loading": "Yükleniyor...",
    "error": "Bir hata oluştu",
    "save": "Kaydet",
    "cancel": "İptal",
    "delete": "Sil",
    "edit": "Düzenle",
    "search": "Ara",
    "noResults": "Sonuç bulunamadı",
    "welcome": "Hoş geldiniz",
    "getStarted": "Başla"
  },
  "auth": {
    "login": "Giriş Yap",
    "logout": "Çıkış Yap",
    "register": "Kayıt Ol",
    "email": "E-posta",
    "password": "Şifre",
    "createAccount": "Hesap Oluştur"
  },
  "errors": {
    "unauthorized": "Yetkilendirme gerekli",
    "forbidden": "Bu işlemi yapmaya yetkiniz yok",
    "notFound": "{{resource}} bulunamadı",
    "serverError": "Sunucu hatası oluştu"
  },
  "validation": {
    "required": "Bu alan zorunludur",
    "invalidEmail": "Geçerli bir e-posta adresi girin",
    "minLength": "En az {{min}} karakter olmalıdır"
  }
}
```

---

## Best Practices

### Key Naming

- Use namespaced keys: `auth.login.button`
- Keep keys semantic, not content-based
- Use camelCase for consistency
- Group related translations

### Type Safety

- Always import types from `@boilerplate/types`
- Use `SupportedLocale` type, not string
- Use `isLocaleSupported()` for runtime checks

### API Responses

- Include `messageKey` for client-side override
- Let backend handle translation based on Accept-Language
- Support query param `?lang=en` for testing

### Performance

- Backend: Translations loaded once at startup
- Frontend: Per-locale bundle splitting via Next.js
- Mobile: AsyncStorage caches language preference

---

## Scripts

### Check Missing Translations

```bash
# Python script to find missing keys
python scripts/check-translations.py packages/frontend/nextjs-template/src/messages
```

### Sync Translation Files

```bash
# Sync missing keys from base locale (tr) to others
python scripts/sync-translations.py packages/frontend/nextjs-template/src/messages tr
```

---

## Checklist

### Initial Setup (Done in Boilerplate)

- [x] Shared types in `@boilerplate/types`
- [x] Backend i18n service and middleware
- [x] Frontend next-intl with locale routing
- [x] Mobile react-i18next with AsyncStorage

### For New Project

- [ ] Review/customize default translations
- [ ] Add project-specific namespaces
- [ ] Add additional languages if needed
- [ ] Configure error codes for domain-specific errors

### For New Feature

- [ ] Extract all user-facing strings
- [ ] Add keys to all locale files
- [ ] Test both locales
- [ ] Verify formatting (dates, numbers, pluralization)
