first commit
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# `cloudflare-ip-geo.ts` — Cloudflare IP-geo detector
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`cloudflareIpGeoDetector(options?)` builds a `LocaleDetector` that
|
||||
maps the Cloudflare `cf-ipcountry` header (ISO 3166-1 alpha-2) to
|
||||
a BCP-47 locale. It is the lowest-precedence detector in the
|
||||
canonical chain — only consulted when no querystring, cookie,
|
||||
storage, or browser preference signal exists — because country
|
||||
geolocation is at best a coarse approximation of language
|
||||
preference (the UK and India both speak English; Switzerland has
|
||||
four official languages).
|
||||
|
||||
Resolution order, per ISO code:
|
||||
|
||||
1. `options.countryToLocale[code]` if provided.
|
||||
2. `${language}-${code}` where `language` is the first entry from
|
||||
the country record's `languages` array in
|
||||
`@suaveplan/locale-data`.
|
||||
3. `null` when neither override nor language data exists.
|
||||
|
||||
## Features
|
||||
|
||||
- Default header `"cf-ipcountry"`, configurable for non-Cloudflare
|
||||
edge providers.
|
||||
- Per-country override map wins over the data-derived default.
|
||||
- `environment: "server"` — automatic browser skip via the chain
|
||||
runner.
|
||||
- Lowercased header values are upper-cased before lookup so
|
||||
`Headers.get` casing quirks don't break resolution.
|
||||
- Missing header, empty header, and unknown country codes all
|
||||
return `null`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { cloudflareIpGeoDetector } from "@suaveplan/i18n-detector/detectors/cloudflare-ip-geo";
|
||||
|
||||
const detector = cloudflareIpGeoDetector();
|
||||
const locale = detector.detect?.({
|
||||
request: {
|
||||
headers: new Headers({ "cf-ipcountry": "AE" }),
|
||||
url: new URL("https://example.com/"),
|
||||
},
|
||||
supportedLocales: ["en-GB", "ar-AE"],
|
||||
fallbackLocale: "en-GB",
|
||||
});
|
||||
// locale === "ar-AE"
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { cloudflareIpGeoDetector } from "@suaveplan/i18n-detector/detectors/cloudflare-ip-geo";
|
||||
|
||||
const detector = cloudflareIpGeoDetector({
|
||||
headerName: "x-vercel-ip-country",
|
||||
countryToLocale: {
|
||||
CH: "de-CH", // Swiss German default — overrides Latin order
|
||||
BE: "nl-BE",
|
||||
IN: "en-IN",
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `cloudflareIpGeoDetector(options?): LocaleDetector` —
|
||||
`environment: "server"`, `name: "cloudflareIpGeo"`. Has only
|
||||
`detect`; no `detectAsync`, no `cache`.
|
||||
- `CloudflareIpGeoDetectorOptions.headerName?: string` — default
|
||||
`"cf-ipcountry"`.
|
||||
- `CloudflareIpGeoDetectorOptions.countryToLocale?: Readonly<Record<string, string>>`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `getAllCountries()` is called once at detector-build time and
|
||||
the result is captured in closure scope; subsequent `detect`
|
||||
calls reuse the same record. Each detector instance therefore
|
||||
has O(1) lookup cost.
|
||||
- The `${primaryLanguage}-${code}` template is correct for the
|
||||
vast majority of countries. The override map exists precisely
|
||||
for the multilingual countries where a single primary language
|
||||
would mis-serve users.
|
||||
Reference in New Issue
Block a user