All files / app/utils getBrowserLocaleFromHeader.ts

100% Statements 13/13
100% Branches 3/3
100% Functions 4/4
100% Lines 11/11

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41                                      17x   14x     33x 33x   23x   14x 18x 277x   18x     4x     17x  
/**
 * Detects the user's preferred locale from the Accept-Language header
 *
 * Parses the Accept-Language header (e.g., "en-US,en;q=0.9,fr;q=0.8"),
 * extracts language codes with their quality values, and returns the first
 * supported locale based on priority.
 *
 * @param acceptLanguage - The Accept-Language header value from the browser
 * @param supportedLocales - Array of supported locale codes (e.g., ["en", "fr", "de"])
 * @returns The best matching supported locale, or undefined if no match found
 *
 * @example
 * getBrowserLocaleFromHeader("fr-FR,fr;q=0.9,en;q=0.8", ["en", "fr", "de"])
 * // Returns "fr"
 */
function getBrowserLocaleFromHeader(
  acceptLanguage: string | undefined,
  supportedLocales: string[],
): string | undefined {
  if (!acceptLanguage) return undefined;
 
  const languages = acceptLanguage
    .split(",")
    .map((lang) => {
      const [code, q = "1"] = lang.trim().split(";q=");
      return { code: code.split("-")[0], quality: parseFloat(q) };
    })
    .sort((a, b) => b.quality - a.quality);
 
  for (const lang of languages) {
    const match = supportedLocales.find((supported) =>
      supported.startsWith(lang.code),
    );
    if (match) return match;
  }
 
  return undefined;
}
 
export { getBrowserLocaleFromHeader };