/** * Shared BCP-47 locale utilities — pure, Node-safe (no DOM). * * Aligns the browser runtime with the rest of the stack (see the locale * standard): * - tm-server accepts BCP-47, canonicalizes to ISO 639-2, and TRUNCATES the * region on the content API (`pt-BR → por`); it does NO cross-locale * fallback. * - `@lionrapid/edge` already preserves full tags with the grammar mirrored * below and negotiates Accept-Language by q-weight. * * Core previously chopped every tag to its 2-letter primary subtag, so a * configured `data-languages="pt-BR,…"` never matched a detected `pt-BR` * (it became `pt`) and fell back to the default. These helpers preserve the * full tag for matching / URLs / display while still negotiating down to the * primary subtag when that is the best available match — the same rule edge * uses, so the two packages agree. * * @module utils/locale */ /** * Locale grammar: 2–3 letter language + zero or more `-XXXX` subtags * (script/region/variant), each 2–4 alphanumerics. Anchored, case-insensitive. * * DELIBERATELY BROADER THAN EDGE, and the difference is not parity rot. This * used to claim it was the "same grammar as `@lionrapid/edge`'s `LOCALE_BODY`"; * that was false — edge uses `?` (AT MOST ONE subtag) where this uses `*`, so * `zh-Hant-TW` and `de-CH-1901` are accepted here and rejected there. * * The claim was corrected rather than the regex: language-script-region is * ordinary BCP-47, so narrowing core to one subtag would reject legitimate tags * to match a narrower peer. The practical consequence to know is that a * two-subtag tag core accepts will not round-trip through edge's cookie/path * matching — if that ever needs to hold, widen edge, do not narrow this. */ export declare const LOCALE_TAG_REGEX: RegExp; /** The primary language subtag, lowercased: `pt-BR → pt`, `ZH-Hant → zh`. */ export declare function primarySubtag(tag: string): string; /** * Canonicalize BCP-47 casing: language lowercase, script Title-case (4 chars), * region UPPERCASE (2 chars). `pt-br → pt-BR`, `ZH-hant → zh-Hant`. Casing * only — subtags are not validated here (that's `matchLocale`/the grammar). */ export declare function normalizeLocale(tag: string): string; /** * ISO 639-2/T -> 639-1 for the languages this product ships. * * MUST stay in lockstep with `ISO639_2_TO_1` in * packages/edge/src/language-detector.ts and `LocalePattern::ISO639_2_TO_1` in * packages/wordpress/src/Runtime/LocalePattern.php — same table, same reason. * All three are asserted identical by * src/__tests__/parity/locale-table-parity.test.ts, which reads the other two * as text; see that file for what a test can and cannot enforce here. * * Curated rather than derived: there is no safe rule for reducing a 3-letter * code to a 2-letter one. `est` (Estonian) would become `es` (Spanish) under * any prefix heuristic. An unlisted code simply does not match, so this can * only widen matching, never mis-match. * * It is needed because tm-server canonicalises to ISO 639-2 (`eng`, `fin`) * while configured language lists carry 2-letter codes, and a `lionrapid_locale` * cookie already in a visitor's browser cannot be recalled. */ export declare const ISO639_2_TO_1: Readonly>; /** * Every form of `tag` worth testing against a configured language list, most * specific first: longest-prefix reduction (`pt-br-x` -> `pt-br` -> `pt`) with * each 3-letter primary subtag also standing for its 2-letter form. * * Mirrors edge's `resolveLocale` candidate generation and WordPress's * `LocalePattern::candidates`. Used for BOTH the cookie and the browser * negotiation paths, so an explicit choice is never resolved less tolerantly * than an inferred preference — the rule the other two surfaces already follow. */ export declare function localeCandidates(tag: string): string[]; /** * Negotiate a locale: for each requested tag (in preference order), return the * best `available` match, trying `localeCandidates(tag)` most-specific first — * exact full tag (case-insensitive), its ISO 639-2 alias if it has one, then the * shortened prefixes down to the bare primary subtag. Returns `fallback` when * nothing matches. Mirrors edge's `resolveLocale` so both packages resolve * identically. * * Note (matches edge + the server): a bare requested `pt` does NOT match an * available `pt-BR` — region-specific availability is not silently widened. * Widening only ever goes the other way (`por`/`pt-BR` -> `pt`). */ export declare function matchLocale(requested: string[], available: string[], fallback: string): string; /** * Parse an `Accept-Language` header into tags ordered by q-weight (desc). * `fr;q=0.8,en;q=0.9 → ['en','fr']`. Missing q defaults to 1.0; a malformed q * sinks to the tail; `*` and empty tags are dropped. */ export declare function parseAcceptLanguage(header: string): string[]; /** * Text direction for a locale — `'rtl'` for Arabic/Persian/Hebrew/Urdu/ * Yiddish, else `'ltr'`. Mirrors tm-server `is_rtl`. */ export declare function localeDir(tag: string): 'ltr' | 'rtl'; //# sourceMappingURL=locale.d.ts.map