first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
# `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
```ts
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
```ts
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.