# At most one i18n library

- **Pattern:** `ab-000235` (`ai-slop-code-drift.convention-drift.dual-i18n-library`)
- **Severity:** info
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000235
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000235)

## Why it matters

Two i18n libraries fragment translation keys across two message-catalog formats, double the translation-team workflow, and produce two pluralization strategies that disagree on edge cases (zero counts, languages with dual/trial grammatical number). Translators receive exports from both systems and inevitably miss keys, so some UI strings render in the user's language while others fall back to the source locale. Build output ships both runtimes to every locale bundle.

## Severity rationale

Info because the impact is translation-workflow drag and partial-translation bugs, not functional or security failures.

## Remediation

Pick one based on your framework: `next-intl` for Next.js App Router, `react-i18next` for plain React, `@lingui/react` if you want compile-time extraction. Migrate call sites with `grep -rl "from 'react-i18next'" src/`, consolidate message catalogs under `src/i18n/messages/` (one file per locale), and remove the loser:

```bash
npm uninstall react-i18next i18next i18next-browser-languagedetector
```

Update your translation export script in `scripts/i18n/` to emit a single catalog format.

## Detection

- **ID:** `dual-i18n-library`
- **Severity:** `info`
- **What to look for:** Apply the three-condition rule against this exact i18n allowlist: `next-intl`, `next-i18next`, `react-i18next`, `i18next`, `@lingui/core`, `@lingui/react`, `react-intl`, `formatjs`, `paraglide-js`, `@formatjs/intl`. Treat `i18next` + `react-i18next` as a single unit. Count all i18n libraries that meet the three conditions (at least 3 importing files each).
- **Pass criteria:** 0 or 1 i18n libraries from the allowlist actively used. Report even on pass: "Canonical i18n library: [name or 'none'] ([N] importing files)."
- **Fail criteria:** 2 or more i18n libraries from the allowlist meet all three conditions (at least 1 non-escape-hatch importing file).
- **Skip (N/A) when:** 0 i18n libraries from the allowlist appear in `RUNTIME_DEPS`.
- **Detail on fail:** `"2 active i18n libraries: 'next-intl' (4 files) AND 'react-i18next' (3 files). Pick one for translation handling."`
- **Remediation:** Two i18n libraries means translations split across systems, two key formats, two pluralization rules. Pick one and migrate:

  ```bash
  # Inventory which library each translation key uses
  grep -rl "from 'next-intl'" src/
  grep -rl "from 'react-i18next'" src/

  # Pick the one with more usage and convert the rest
  npm uninstall react-i18next i18next
  ```

Taxons: dependency-coherence

HTML version: https://auditbuffet.com/patterns/ab-000235
