Skip to content
26 changes: 22 additions & 4 deletions fluent-langneg/src/accepted_languages.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
export default function acceptedLanguages(string = "") {
if (typeof string !== "string") {
function parseAcceptLanguageEntry(entry) {
const langWithQ = entry.split(";").map(u => u.trim());
let q = 1.0;
if (langWithQ.length > 1) {
const qVal = langWithQ[1].split("=").map(u => u.trim());
if (qVal.length === 2 && qVal[0].toLowerCase() === "q") {
const qn = Number(qVal[1]);
q = isNaN(qn) ? 0.0 : qn;
}
}
return { lang: langWithQ[0], q };
}

export default function acceptedLanguages(acceptLanguageHeader = "") {
if (typeof acceptLanguageHeader !== "string") {
throw new TypeError("Argument must be a string");
}
const tokens = string.split(",").map(t => t.trim());
return tokens.filter(t => t !== "").map(t => t.split(";")[0]);
const tokens = acceptLanguageHeader.split(",").map(t => t.trim())
.filter(t => t !== "");
const langsWithQ = Array.from(tokens.map(parseAcceptLanguageEntry).entries());
// order by q descending, keeping the header order for equal weights
langsWithQ.sort(([aidx, aval], [bidx, bval]) =>
aval.q === bval.q ? aidx - bidx : bval.q - aval.q);
return langsWithQ.map(([, val]) => val.lang);
}
53 changes: 53 additions & 0 deletions fluent-langneg/test/headers_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import assert from 'assert';
import acceptedLanguages from '../src/accepted_languages';

suite('parse headers', () => {
test('without an argument', () => {
assert.deepStrictEqual(
acceptedLanguages(), []
);
});

test('without quality values', () => {
assert.deepStrictEqual(
acceptedLanguages('en-US, fr, pl'), [
Expand Down Expand Up @@ -29,6 +35,53 @@ 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',
'fr-CH',
'de',
'*',
'en'
]
);
});

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,
Expand Down