Agent SkillsAgent Skills
lyzno1

i18next

@lyzno1/i18next
lyzno1
1
0 forks
Updated 4/7/2026
View on GitHub

i18next internationalization patterns for the Flux project. Flat keys, type-safe namespaces, on-demand loading, Intl-based formatting, and i18next-cli toolchain. Use when adding translation keys, creating namespaces, using useTranslation, formatting numbers/dates/durations, working with pluralization, running i18n extraction/validation, or debugging CLI issues. Triggers on tasks involving i18n, translations, localization, language switching, locale files, or i18next-cli.

Installation

$npx agent-skills-cli install @lyzno1/i18next
Claude Code
Cursor
Copilot
Codex
Antigravity

Details

Repositorylyzno1/flux
Path.agents/skills/i18next/SKILL.md
Branchmain
Scoped Name@lyzno1/i18next

Usage

After installing, this skill will be available to your AI coding assistant.

Verify installation:

npx agent-skills-cli list

Skill Instructions


name: i18next description: i18next internationalization patterns for the Flux project. Flat keys, type-safe namespaces, on-demand loading, Intl-based formatting, and i18next-cli toolchain. Use when adding translation keys, creating namespaces, using useTranslation, formatting numbers/dates/durations, working with pluralization, running i18n extraction/validation, or debugging CLI issues. Triggers on tasks involving i18n, translations, localization, language switching, locale files, or i18next-cli.

i18next

Type-safe i18n with flat keys, on-demand namespace loading, Intl-based formatting, and i18next-cli for automated key management.

Architecture

  • Shared Config: apps/web/src/i18n/config.ts โ€” defaultNS, fallbackLng, keySeparator, language list shared by runtime + CLI
  • Runtime Config: apps/web/src/i18n/index.ts โ€” i18next init, backend loader, language detector, custom formatters
  • CLI Config: apps/web/i18next.config.ts โ€” i18next-cli extraction and validation settings
  • Types: apps/web/src/i18n/i18next.d.ts โ€” module augmentation for type-safe t(); defaultNS/keySeparator inferred from shared config
  • Locales: apps/web/src/locales/{en-US,zh-CN}/{namespace}.json
  • Loading: i18next-resources-to-backend dynamic import. The common namespace is preloaded at init via ns: ["common"]. Route-scoped namespaces (e.g. auth) are preloaded via TanStack Router loader (e.g. _auth.tsx). Other namespaces are loaded on demand by useTranslation("ns") with useSuspense: true (caught by TanStack Router's per-route Suspense boundary from defaultPendingComponent)
apps/web/
โ”œโ”€โ”€ i18next.config.ts   # CLI config (extraction, validation)
โ””โ”€โ”€ src/
    โ”œโ”€โ”€ i18n/
    โ”‚   โ”œโ”€โ”€ config.ts       # shared i18n constants (runtime + CLI + types)
    โ”‚   โ”œโ”€โ”€ index.ts        # runtime init + custom formatters
    โ”‚   โ””โ”€โ”€ i18next.d.ts    # type augmentation (update when adding namespace)
    โ””โ”€โ”€ locales/
        โ”œโ”€โ”€ en-US/          # source of truth for types
        โ”‚   โ”œโ”€โ”€ common.json # default namespace
        โ”‚   โ”œโ”€โ”€ auth.json
        โ”‚   โ”œโ”€โ”€ ai.json
        โ”‚   โ””โ”€โ”€ dify.json
        โ””โ”€โ”€ zh-CN/          # must have same keys as en-US (except plural variants)

i18next-cli

Automated key extraction, unused key detection, and multi-language sync. See references/cli.md for full configuration and command details.

Commands

Run from apps/web/:

pnpm i18n          # extract keys from code, remove unused keys, sync languages
pnpm i18n:ci       # CI mode โ€” fails if translation files need updating
pnpm i18n:status   # translation health report by namespace/language
pnpm i18n:sync     # sync secondary languages against primary (en-US)
pnpm i18n:lint     # detect hardcoded strings that should be translated

CLI-Compatible Code Patterns

CRITICAL โ€” the CLI uses SWC static AST analysis. It can only extract t() calls with string literals. Violating these rules causes keys to be misidentified or deleted.

// โœ… CLI extracts correctly โ€” string literal
t("nav.home")

// โŒ CLI CANNOT extract โ€” variable
t(someVariable)

// โŒ CLI CANNOT trace namespace โ€” t passed as function parameter
const helper = (t: TFunction) => t("key")

Rules to keep CLI happy:

  1. Always use string literals in t() calls โ€” never t(variable) or t(computedKey)
  2. Keep t() calls inside useTranslation() scope โ€” the CLI traces useTranslation("ns") to determine which namespace a key belongs to. If t is passed as a parameter to a utility function outside the component, the CLI loses namespace tracking and assigns keys to common (default)
  3. When a utility function needs translated strings, pass the strings not the t function:
// โŒ WRONG โ€” CLI cannot trace t's namespace
const getLabel = (t: TFunction) => t("my.key");

// โœ… RIGHT โ€” t() called inside component scope, plain strings passed out
const { t } = useTranslation("ai");
const label = getLabel({ myKey: t("my.key") });

Key Naming Rules

CRITICAL โ€” keys are flat strings, NOT nested paths (keySeparator: false).

{ "nav.home": "Home", "nav.dashboard": "Dashboard" }
  • Use category.item grouping within a namespace: signIn.email, validation.passwordMin
  • Use camelCase: emptyState, apiMessage
  • Plurals use _one / _other suffixes: eventsCount_one, eventsCount_other
  • Never prefix keys with namespace name โ€” auth.json keys must not start with auth.
  • Never use nested JSON โ€” { "nav": { "home": "..." } } is wrong

Adding a New Namespace

  1. Create locales/en-US/{ns}.json and locales/zh-CN/{ns}.json
  2. If the namespace is used in the root layout (outside route components, e.g. in __root.tsx, Header, AuthBrandPanel), add it to the ns array in i18n/index.ts for preloading. Route-level namespaces do NOT need this โ€” useSuspense: true + TanStack Router's per-route Suspense handles them automatically.
  3. Add import + resource entry in i18next.d.ts:
import type settings from "../locales/en-US/settings.json";
// inside CustomTypeOptions.resources:
settings: typeof settings;
  1. Run pnpm i18n to validate extraction

Adding Keys to Existing Namespace

  1. Add key to locales/en-US/{ns}.json (source of truth)
  2. Add key to locales/zh-CN/{ns}.json
  3. JSON keys must be alphabetically sorted (enforced by Biome useSortedKeys)
  4. JSON must use tab indentation (enforced by Biome)
  5. Run pnpm i18n:ci to verify โ€” or just write t("newKey") in code and run pnpm i18n to auto-generate

Usage Patterns

// React component โ€” namespace via hook
const { t } = useTranslation("auth");
t("signIn.email");

// Default namespace (common)
const { t } = useTranslation();
t("nav.home");

// With interpolation
t("welcome", { name: "Alice" }); // "Welcome Alice"

// With count (auto plural resolution)
t("items", { count: 5 }); // selects _one or _other based on CLDR

Pluralization

English needs _one + _other. Chinese only needs _other (CLDR rules).

// en-US
{ "items_one": "{{count}} item", "items_other": "{{count}} items" }
// zh-CN
{ "items_other": "{{count}} ไธช้กน็›ฎ" }

Call with base key โ€” i18next resolves the suffix automatically:

t("items", { count: 5 });

Formatting (built-in Intl API)

{
  "count": "{{val, number}}",
  "price": "{{val, currency(USD)}}",
  "date": "{{val, datetime}}",
  "elapsed": "{{val, duration}}"
}

Custom duration formatter in index.ts โ€” accepts milliseconds, outputs locale-aware seconds.

Rules

  1. Always use string literal t() calls โ€” CLI cannot extract dynamic keys
  2. Keep t() inside useTranslation() scope โ€” pass translated strings to helpers, not the t function
  3. Never prefix keys with namespace name โ€” namespace is already the file
  4. Keep JSON keys sorted alphabetically โ€” pnpm check enforces this
  5. zh-CN must match en-US keys โ€” except _one (Chinese doesn't need it)
  6. Always pass raw values to formatters โ€” let i18next handle conversion
  7. Run pnpm i18n:ci before committing โ€” or let CI catch it