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,88 @@
# querystringDetector
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`querystringDetector` is a `LocaleDetector` that reads the active locale
from a URL search parameter. It runs in both browser and server
environments, making it the canonical first link in the default detector
chain — explicit user intent expressed through `?lang=fr-FR` always
overrides every other source (cookie, localStorage, geo lookup, …) per
the spec scenario "querystring overrides cookie".
The detector is intentionally minimal: it never persists a value (so
sharing a `?lang=…` link does not pollute the user's stored preference),
it does not throw when the param is absent, and it never reaches into
window-level globals when a server-side `request` context is supplied.
## Features
- Configurable parameter name via `opts.paramName` (default `"lang"`)
- Server input via `ctx.request.url.searchParams`
- Browser input via `window.location.search`
- Returns `null` for missing params, empty values, or environments
where neither input source exists
- `environment: "both"` — eligible on every chain run
## Usage: Basic Example
```ts
import { querystringDetector } from "@suaveplan/react-i18n/detector/querystring/querystring";
import { createDetectorChain } from "@suaveplan/i18n/detector";
const chain = createDetectorChain([querystringDetector()]);
const result = await chain.detect({
request: {
headers: new Headers(),
url: new URL("https://example.com/?lang=fr-FR"),
},
responseHeaders: new Headers(),
supportedLocales: ["en-GB", "fr-FR"],
fallbackLocale: "en-GB",
});
// → { locale: "fr-FR", detector: "querystring", rawValue: "fr-FR" }
```
## Usage: Advanced Example
```ts
import { querystringDetector } from "@suaveplan/react-i18n/detector/querystring/querystring";
import { defaultDetectorChain } from "@suaveplan/react-i18n/detector/default-chain/default-chain";
// Replace the default `?lang=` entry with `?locale=` for a host app
// that already uses that parameter, while keeping every other detector.
const chain = defaultDetectorChain({ fallback: "en-GB" }).map((d) =>
d.name === "querystring" ? querystringDetector({ paramName: "locale" }) : d
);
```
## API Reference
### `querystringDetector(options?: QuerystringDetectorOptions): LocaleDetector`
| Param | Type | Default | Description |
| ------- | ----------------------------- | ------- | -------------------------------------------- |
| options | `QuerystringDetectorOptions?` | `{}` | Optional `{ paramName?: string }` overrides. |
Returns a `LocaleDetector` with `name: "querystring"` and
`environment: "both"`. The detector exposes only `detect(ctx)`; it does
not implement `cache(...)` (the URL is never written).
### `QuerystringDetectorOptions`
| Field | Type | Default | Description |
| --------- | -------- | -------- | ---------------------------------------- |
| paramName | `string` | `"lang"` | Search-parameter name the detector reads |
## Implementation Notes
- The detector strictly prefers `ctx.request` over `window.location` so
SSR contexts that happen to leak a `globalThis.location` (e.g. test
harnesses) do not override the request the application is rendering.
- An empty string value (e.g. `?lang=`) is treated as "no preference"
per the chain contract — the detector falls through, never wins.
- The implementation is allocation-light (one `URLSearchParams` on the
browser path) and synchronous so the chain runner picks it up in the
initial sync pass before any async detector is dispatched.