68 lines
2.7 KiB
JavaScript
68 lines
2.7 KiB
JavaScript
import { getLocaleCodes, isValidLocale } from './helpers.js';
|
|
/** Cookie name the template uses to persist a visitor's locale choice. */ export const LOCALE_COOKIE_NAME = 'ipal-locale';
|
|
/**
|
|
* Resolves which locale to serve, in priority order:
|
|
* 1. Cookie (explicit prior choice)
|
|
* 2. Accept-Language header (best match against configured locales)
|
|
* 3. Configured default locale
|
|
*
|
|
* Pure function — the template feeds it request data and acts on the result
|
|
* (redirect, cookie set). No Next.js or request objects here.
|
|
*/ export function negotiateLocale({ acceptLanguage, config, cookieLocale }) {
|
|
// 1. Explicit prior choice wins
|
|
if (cookieLocale && isValidLocale(cookieLocale, config)) {
|
|
return cookieLocale;
|
|
}
|
|
// 2. Best match from Accept-Language
|
|
const fromHeader = matchAcceptLanguage(acceptLanguage, config);
|
|
if (fromHeader) {
|
|
return fromHeader;
|
|
}
|
|
// 3. Fall back to configured default
|
|
return config.defaultLocale;
|
|
}
|
|
/**
|
|
* Parses an Accept-Language header and returns the best-matching configured
|
|
* locale, or undefined if none match.
|
|
*
|
|
* Matches on the primary subtag (e.g. "en-US" matches configured "en"),
|
|
* respecting the header's quality-value ordering.
|
|
*/ export function matchAcceptLanguage(acceptLanguage, config) {
|
|
if (!acceptLanguage) {
|
|
return undefined;
|
|
}
|
|
const available = getLocaleCodes(config);
|
|
const ranked = parseAcceptLanguage(acceptLanguage);
|
|
for (const tag of ranked){
|
|
// Exact match (e.g. "pt-BR" === "pt-BR")
|
|
const exact = available.find((code)=>code.toLowerCase() === tag);
|
|
if (exact) {
|
|
return exact;
|
|
}
|
|
// Primary-subtag match (e.g. "en-us" → "en")
|
|
const primary = tag.split('-')[0];
|
|
const partial = available.find((code)=>code.toLowerCase().split('-')[0] === primary);
|
|
if (partial) {
|
|
return partial;
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
/**
|
|
* Parses an Accept-Language header into locale tags ordered by descending
|
|
* quality value. Tags are lowercased for comparison.
|
|
*
|
|
* "en-US,en;q=0.9,pl;q=0.8" → ["en-us", "en", "pl"]
|
|
*/ function parseAcceptLanguage(header) {
|
|
return header.split(',').map((part)=>{
|
|
const [tag, ...params] = part.trim().split(';');
|
|
const qParam = params.find((p)=>p.trim().startsWith('q='));
|
|
const quality = qParam ? parseFloat(qParam.split('=')[1]) : 1;
|
|
return {
|
|
quality: Number.isNaN(quality) ? 0 : quality,
|
|
tag: tag.trim().toLowerCase()
|
|
};
|
|
}).filter((entry)=>entry.tag && entry.tag !== '*').sort((a, b)=>b.quality - a.quality).map((entry)=>entry.tag);
|
|
} //# sourceMappingURL=negotiateLocale.js.map
|
|
|
|
//# sourceMappingURL=negotiateLocale.js.map
|