# querystringDetector _(no submodules)_ ## 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.