language.js 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. module.exports = preferredLanguages;
  2. preferredLanguages.preferredLanguages = preferredLanguages;
  3. function parseAcceptLanguage(accept) {
  4. return accept.split(',').map(function(e) {
  5. return parseLanguage(e.trim());
  6. }).filter(function(e) {
  7. return e;
  8. });
  9. }
  10. function parseLanguage(s) {
  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. full: full
  30. };
  31. }
  32. function getLanguagePriority(language, accepted) {
  33. return (accepted.map(function(a){
  34. return specify(language, a);
  35. }).filter(Boolean).sort(function (a, b) {
  36. if(a.s == b.s) {
  37. return a.q > b.q ? -1 : 1;
  38. } else {
  39. return a.s > b.s ? -1 : 1;
  40. }
  41. })[0] || {s: 0, q: 0});
  42. }
  43. function specify(language, spec) {
  44. var p = parseLanguage(language)
  45. if (!p) return null;
  46. var s = 0;
  47. if(spec.full === p.full){
  48. s |= 4;
  49. } else if (spec.prefix === p.full) {
  50. s |= 2;
  51. } else if (spec.full === p.prefix) {
  52. s |= 1;
  53. } else if (spec.full !== '*' ) {
  54. return null
  55. }
  56. return {
  57. s: s,
  58. q: spec.q,
  59. }
  60. };
  61. function preferredLanguages(accept, provided) {
  62. // RFC 2616 sec 14.4: no header = *
  63. accept = parseAcceptLanguage(accept === undefined ? '*' : accept || '');
  64. if (provided) {
  65. var ret = provided.map(function(type) {
  66. return [type, getLanguagePriority(type, accept)];
  67. }).filter(function(pair) {
  68. return pair[1].q > 0;
  69. }).sort(function(a, b) {
  70. var pa = a[1];
  71. var pb = b[1];
  72. if(pa.q == pb.q) {
  73. return pa.s < pb.s ? 1 : -1;
  74. } else {
  75. return pa.q < pb.q ? 1 : -1;
  76. }
  77. }).map(function(pair) {
  78. return pair[0];
  79. });
  80. return ret;
  81. } else {
  82. return accept.sort(function (a, b) {
  83. // revsort
  84. return a.q < b.q ? 1 : -1;
  85. }).filter(function(type) {
  86. return type.q > 0;
  87. }).map(function(type) {
  88. return type.full;
  89. });
  90. }
  91. }