diff --git a/src/lib/get-client-locales.test.ts b/src/lib/get-client-locales.test.ts new file mode 100644 index 0000000..eacba30 --- /dev/null +++ b/src/lib/get-client-locales.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, test } from "bun:test"; +import * as getClientLocales from "./get-client-locales.js"; + +describe(getClientLocales.getClientLocales.name, () => { + test('should not throw on invalid locales', () => { + let headers = new Headers(); + headers.set("Accept-Language", "cs-CZ,cs;q=0.9,true;q=0.8,en-US;q=0.7,en;q=0.6"); + expect(() => getClientLocales.getClientLocales(headers)).not.toThrowError(RangeError) + }); +}); \ No newline at end of file diff --git a/src/lib/get-client-locales.ts b/src/lib/get-client-locales.ts index 79b4416..bcfb207 100644 --- a/src/lib/get-client-locales.ts +++ b/src/lib/get-client-locales.ts @@ -28,11 +28,25 @@ export function getClientLocales(requestOrHeaders: Request | Headers): Locales { // if the header is not defined, return undefined if (!acceptLanguage) return undefined; + let parsedLocales = parse(acceptLanguage) + .filter((lang) => lang.code !== "*") + .map(formatLanguageString); + + let validLocales: string[] = [] + + for (let locale of parsedLocales) { + try { + // This will throw on invalid locales + new Intl.Locale(locale); + + // If we get here, the locale is valid + validLocales.push(locale); + } catch {} + } + let locale = pick( Intl.DateTimeFormat.supportedLocalesOf( - parse(acceptLanguage) - .filter((lang) => lang.code !== "*") - .map(formatLanguageString), + validLocales, ), acceptLanguage, ); diff --git a/src/lib/parser.test.ts b/src/lib/parser.test.ts index af37a25..2729a6a 100644 --- a/src/lib/parser.test.ts +++ b/src/lib/parser.test.ts @@ -85,6 +85,15 @@ describe(parser.parse.name, () => { { code: "en", quality: 0.4, script: null }, { code: "*", quality: 0.1, script: null }, ]); + + let result2 = parser.parse("zh-CN, zh; q=0.9, en; q=0.8, ko; q=0.7"); + + expect(result2).toEqual([ + { code: "zh", region: "CN", quality: 1.0, script: null }, + { code: "zh", quality: 0.9, script: null }, + { code: "en", quality: 0.8, script: null }, + { code: "ko", quality: 0.7, script: null }, + ]); }); test("should sort based on quality value", () => { diff --git a/src/lib/parser.ts b/src/lib/parser.ts index 7de32ee..1b89e2e 100644 --- a/src/lib/parser.ts +++ b/src/lib/parser.ts @@ -1,6 +1,6 @@ import { formatLanguageString } from "./format-language-string.js"; -let REGEX = /((([a-zA-Z]+(-[a-zA-Z0-9]+){0,2})|\*)(;q=[0-1](\.[0-9]+)?)?)*/g; +let REGEX = /[ ]*((([a-zA-Z]+(-[a-zA-Z0-9]+){0,2})|\*)(;[ ]*q=[0-1](\.[0-9]+)?[ ]*)?)*/g; export interface Language { code: string; @@ -25,6 +25,9 @@ export function parse(acceptLanguage?: string): Language[] { for (let m of strings) { if (!m) continue; + + m = m.trim(); + let bits = m.split(";"); let ietf = bits[0]?.split("-") ?? []; let hasScript = ietf.length === 3;