3 * Copyright(c) 2012 Isaac Z. Schlueter
4 * Copyright(c) 2014 Federico Romero
5 * Copyright(c) 2014-2015 Douglas Christopher Wilson
16 module.exports = preferredLanguages;
17 module.exports.preferredLanguages = preferredLanguages;
24 var simpleLanguageRegExp = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
27 * Parse the Accept-Language header.
31 function parseAcceptLanguage(accept) {
32 var accepts = accept.split(',');
34 for (var i = 0, j = 0; i < accepts.length; i++) {
35 var language = parseLanguage(accepts[i].trim(), i);
38 accepts[j++] = language;
49 * Parse a language from the Accept-Language header.
53 function parseLanguage(str, i) {
54 var match = simpleLanguageRegExp.exec(str);
55 if (!match) return null;
57 var prefix = match[1],
61 if (suffix) full += "-" + suffix;
65 var params = match[3].split(';')
66 for (var j = 0; j < params.length; j++) {
67 var p = params[j].split('=');
68 if (p[0] === 'q') q = parseFloat(p[1]);
82 * Get the priority of a language.
86 function getLanguagePriority(language, accepted, index) {
87 var priority = {o: -1, q: 0, s: 0};
89 for (var i = 0; i < accepted.length; i++) {
90 var spec = specify(language, accepted[i], index);
92 if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {
101 * Get the specificity of the language.
105 function specify(language, spec, index) {
106 var p = parseLanguage(language)
109 if(spec.full.toLowerCase() === p.full.toLowerCase()){
111 } else if (spec.prefix.toLowerCase() === p.full.toLowerCase()) {
113 } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {
115 } else if (spec.full !== '*' ) {
128 * Get the preferred languages from an Accept-Language header.
132 function preferredLanguages(accept, provided) {
133 // RFC 2616 sec 14.4: no header = *
134 var accepts = parseAcceptLanguage(accept === undefined ? '*' : accept || '');
137 // sorted list of all languages
141 .map(getFullLanguage);
144 var priorities = provided.map(function getPriority(type, index) {
145 return getLanguagePriority(type, accepts, index);
148 // sorted list of accepted languages
149 return priorities.filter(isQuality).sort(compareSpecs).map(function getLanguage(priority) {
150 return provided[priorities.indexOf(priority)];
159 function compareSpecs(a, b) {
160 return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0;
164 * Get full language string.
168 function getFullLanguage(spec) {
173 * Check if a spec has any quality.
177 function isQuality(spec) {