Files
skills/docs/localization/i18n/i18n-detector/src/chain/bcp47-negotiate.md
2026-05-29 08:20:56 +01:00

2.7 KiB

bcp47-negotiate.ts — default BCP-47 negotiator

Purpose

bcp47Negotiate(raw, supported, fallback) is the default negotiator wired into createDetectorChain when no options.negotiate is supplied. It maps a detector's raw locale output onto a value the application actually supports without forcing the configured fallback prematurely. Returning null on miss is intentional — the chain advances to the next detector instead of committing to the fallback before every detector has been consulted.

The implementation handles the three cases the upstream LocaleResolver.resolve() happy path covered: exact match, short-code expansion via LOCALE_BCP47_MAP from @suaveplan/i18n-shared, and language-subtag fallback.

Features

  • Exact-match short-circuit: returns the raw input unchanged when it appears in supported.
  • Short-code expansion via LOCALE_BCP47_MAP ("en" → "en-GB").
  • Language-subtag matching falls back to the first supported entry whose primary language matches the raw input's primary language.
  • Strict null on miss — the negotiator never invents a value.
  • Pure function, no internal state, safe to call from any context.

Usage: Basic Example

import { bcp47Negotiate } from "@suaveplan/i18n-detector/chain";

bcp47Negotiate("fr-FR", ["en-GB", "fr-FR"], "en-GB"); // "fr-FR"
bcp47Negotiate("en", ["en-GB", "fr-FR"], "en-GB"); // "en-GB"
bcp47Negotiate("zh-Hant-TW", ["zh-CN", "en-GB"], "en-GB"); // "zh-CN"
bcp47Negotiate("xx-XX", ["en-GB"], "en-GB"); // null

Usage: Advanced Example

import {
    bcp47Negotiate,
    createDetectorChain,
    type NegotiateFn,
} from "@suaveplan/i18n-detector";

// Wrap the default with a per-request override map.
const negotiate: NegotiateFn = (raw, supported, fallback) => {
    if (raw === "zh-Hans") return "zh-CN";
    return bcp47Negotiate(raw, supported, fallback);
};

const chain = createDetectorChain(detectors, { negotiate });

API Reference

  • bcp47Negotiate: NegotiateFn — pure function; (raw: string, supported: readonly string[], fallback: string) => string | null.
  • Returns null when input is empty, non-string, or no supported match exists.
  • The fallback argument is accepted for parity with the NegotiateFn signature but is intentionally unused — the chain decides the fallback policy.

Implementation Notes

  • Lookup order: exact → lower-cased short-code expansion → original short-code expansion → language-subtag scan.
  • LOCALE_BCP47_MAP is consulted twice (lowered first, raw form as a fallback) so mixed-case inputs like "En-GB" resolve via the same lookup path as "en".
  • Whitespace-only and empty inputs return null before any lookup runs.