diff --git a/fluent-langneg/src/accepted_languages.ts b/fluent-langneg/src/accepted_languages.ts index 80c03e87..2a232ad3 100644 --- a/fluent-langneg/src/accepted_languages.ts +++ b/fluent-langneg/src/accepted_languages.ts @@ -1,7 +1,20 @@ -export function acceptedLanguages(str: string = ""): Array { - if (typeof str !== "string") { +const entryRegexp = + // locale ; q = qval + /(?:^|,)([^,;]+)(?:;\s*[qQ]\s*=([^,;]+))?/g; + +export function acceptedLanguages(acceptLanguageHeader = ""): string[] { + if (typeof acceptLanguageHeader !== "string") { throw new TypeError("Argument must be a string"); } - const tokens = str.split(",").map(t => t.trim()); - return tokens.filter(t => t !== "").map(t => t.split(";")[0]); + + const langsWithQ: Array<{ lang: string; q: number; index: number }> = []; + for (const token of acceptLanguageHeader.matchAll(entryRegexp)) { + const q = token[2] ? parseFloat(token[2]) || 0 : 1; + langsWithQ.push({ lang: token[1].trim(), q, index: token.index }); + } + + // order by q descending, keeping the header order for equal weights + langsWithQ.sort((a, b) => (a.q === b.q ? a.index - b.index : b.q - a.q)); + + return langsWithQ.map(val => val.lang); } diff --git a/fluent-langneg/test/headers_test.js b/fluent-langneg/test/headers_test.js index 3561340b..bb50aaf5 100644 --- a/fluent-langneg/test/headers_test.js +++ b/fluent-langneg/test/headers_test.js @@ -18,6 +18,36 @@ suite("parse headers", () => { ); }); + test("with out of order quality values", () => { + assert.deepStrictEqual( + acceptedLanguages("en;q=0.8, fr;q=0.9, de;q=0.7, *;q=0.5, fr-CH"), + ["fr-CH", "fr", "en", "de", "*"] + ); + }); + + test("with equal q values", () => { + assert.deepStrictEqual( + acceptedLanguages("en;q=0.1, fr;q=0.1, de;q=0.1, *;q=0.1"), + ["en", "fr", "de", "*"] + ); + }); + + test("with duff q values", () => { + assert.deepStrictEqual( + acceptedLanguages( + "en;q=no, fr;z=0.9, de;q=0.7;q=9, *;q=0.5, fr-CH;q=a=0.1" + ), + ["fr", "de", "*", "en", "fr-CH"] + ); + }); + + test("with empty entries", () => { + assert.deepStrictEqual( + acceptedLanguages("en;q=0.8,,, fr;q=0.9,, de;q=0.7, *;q=0.5, fr-CH"), + ["fr-CH", "fr", "en", "de", "*"] + ); + }); + test("edge cases", () => { const args = [null, NaN, Infinity, [], {}];