language.js 2.5 KB

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