You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

100 lines
2.3 KiB

7 years ago
  1. module.exports = preferredLanguages;
  2. preferredLanguages.preferredLanguages = preferredLanguages;
  3. function parseAcceptLanguage(accept) {
  4. return accept.split(',').map(function(e, i) {
  5. return parseLanguage(e.trim(), i);
  6. }).filter(function(e) {
  7. return e;
  8. });
  9. }
  10. function parseLanguage(s, i) {
  11. var match = s.match(/^\s*(\S+?)(?:-(\S+?))?\s*(?:;(.*))?$/);
  12. if (!match) return null;
  13. var prefix = match[1],
  14. suffix = match[2],
  15. full = prefix;
  16. if (suffix) full += "-" + suffix;
  17. var q = 1;
  18. if (match[3]) {
  19. var params = match[3].split(';')
  20. for (var i = 0; i < params.length; i ++) {
  21. var p = params[i].split('=');
  22. if (p[0] === 'q') q = parseFloat(p[1]);
  23. }
  24. }
  25. return {
  26. prefix: prefix,
  27. suffix: suffix,
  28. q: q,
  29. i: i,
  30. full: full
  31. };
  32. }
  33. function getLanguagePriority(language, accepted) {
  34. return (accepted.map(function(a){
  35. return specify(language, a);
  36. }).filter(Boolean).sort(function (a, b) {
  37. if(a.s == b.s) {
  38. return a.q > b.q ? -1 : 1;
  39. } else {
  40. return a.s > b.s ? -1 : 1;
  41. }
  42. })[0] || {s: 0, q: 0});
  43. }
  44. function specify(language, spec) {
  45. var p = parseLanguage(language)
  46. if (!p) return null;
  47. var s = 0;
  48. if(spec.full.toLowerCase() === p.full.toLowerCase()){
  49. s |= 4;
  50. } else if (spec.prefix.toLowerCase() === p.full.toLowerCase()) {
  51. s |= 2;
  52. } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {
  53. s |= 1;
  54. } else if (spec.full !== '*' ) {
  55. return null
  56. }
  57. return {
  58. s: s,
  59. q: spec.q,
  60. }
  61. };
  62. function preferredLanguages(accept, provided) {
  63. // RFC 2616 sec 14.4: no header = *
  64. accept = parseAcceptLanguage(accept === undefined ? '*' : accept || '');
  65. if (provided) {
  66. var ret = provided.map(function(type) {
  67. return [type, getLanguagePriority(type, accept)];
  68. }).filter(function(pair) {
  69. return pair[1].q > 0;
  70. }).sort(function(a, b) {
  71. var pa = a[1];
  72. var pb = b[1];
  73. return (pb.q - pa.q) || (pb.s - pa.s) || (pa.i - pb.i);
  74. }).map(function(pair) {
  75. return pair[0];
  76. });
  77. return ret;
  78. } else {
  79. return accept.sort(function (a, b) {
  80. // revsort
  81. return (b.q - a.q) || (a.i - b.i);
  82. }).filter(function(type) {
  83. return type.q > 0;
  84. }).map(function(type) {
  85. return type.full;
  86. });
  87. }
  88. }