inflection.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. * Copyright (c) 2010 George Moschovitis, http://www.gmosx.com
  3. *
  4. * Permission is hereby granted, free of charge, to any person
  5. * obtaining a copy of this software and associated documentation
  6. * files (the "Software"), to deal in the Software without
  7. * restriction, including without limitation the rights to use,
  8. * copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the
  10. * Software is furnished to do so, subject to the following
  11. * conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be
  14. * included in all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  18. * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
  20. * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  21. * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  22. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23. * SOFTWARE.
  24. *
  25. * A port of the Rails/ActiveSupport Inflector class
  26. * http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html
  27. */
  28. /**
  29. @name inflection
  30. @namespace inflection
  31. */
  32. var inflection = new (function () {
  33. /**
  34. @name inflection#inflections
  35. @public
  36. @object
  37. @description A list of rules and replacements for different inflection types
  38. */
  39. this.inflections = {
  40. plurals: []
  41. , singulars: []
  42. , uncountables: []
  43. };
  44. var self = this
  45. , setInflection
  46. , setPlural
  47. , setSingular
  48. , setUncountable
  49. , setIrregular;
  50. // Add a new inflection rule/replacement to the beginning of the array for the
  51. // inflection type
  52. setInflection = function (type, rule, replacement) {
  53. self.inflections[type].unshift([rule, replacement]);
  54. };
  55. // Add a new plural inflection rule
  56. setPlural = function (rule, replacement) {
  57. setInflection('plurals', rule, replacement);
  58. };
  59. // Add a new singular inflection rule
  60. setSingular = function (rule, replacement) {
  61. setInflection('singulars', rule, replacement);
  62. };
  63. // Add a new irregular word to the inflection list, by a given singular and plural inflection
  64. setIrregular = function (singular, plural) {
  65. if (singular.substr(0, 1).toUpperCase() == plural.substr(0, 1).toUpperCase()) {
  66. setPlural(new RegExp("(" + singular.substr(0, 1) + ")" + singular.substr(1) + "$", "i"),
  67. '$1' + plural.substr(1));
  68. setPlural(new RegExp("(" + plural.substr(0, 1) + ")" + plural.substr(1) + "$", "i"),
  69. '$1' + plural.substr(1));
  70. setSingular(new RegExp("(" + plural.substr(0, 1) + ")" + plural.substr(1) + "$", "i"),
  71. '$1' + singular.substr(1));
  72. } else {
  73. setPlural(new RegExp(singular.substr(0, 1).toUpperCase() + singular.substr(1) + "$"),
  74. plural.substr(0, 1).toUpperCase() + plural.substr(1));
  75. setPlural(new RegExp(singular.substr(0, 1).toLowerCase() + singular.substr(1) + "$"),
  76. plural.substr(0, 1).toLowerCase() + plural.substr(1));
  77. setPlural(new RegExp(plural.substr(0, 1).toUpperCase() + plural.substr(1) + "$"),
  78. plural.substr(0, 1).toUpperCase() + plural.substr(1));
  79. setPlural(new RegExp(plural.substr(0, 1).toLowerCase() + plural.substr(1) + "$"),
  80. plural.substr(0, 1).toLowerCase() + plural.substr(1));
  81. setSingular(new RegExp(plural.substr(0, 1).toUpperCase() + plural.substr(1) + "$"),
  82. singular.substr(0, 1).toUpperCase() + singular.substr(1));
  83. setSingular(new RegExp(plural.substr(0, 1).toLowerCase() + plural.substr(1) + "$"),
  84. singular.substr(0, 1).toLowerCase() + singular.substr(1));
  85. }
  86. };
  87. // Add a new word to the uncountable inflection list
  88. setUncountable = function (word) {
  89. self.inflections.uncountables[word] = true;
  90. };
  91. // Create inflections
  92. (function () {
  93. setPlural(/$/, "s");
  94. setPlural(/s$/i, "s");
  95. setPlural(/(ax|test)is$/i, "$1es");
  96. setPlural(/(octop|vir)us$/i, "$1i");
  97. setPlural(/(octop|vir)i$/i, "$1i");
  98. setPlural(/(alias|status)$/i, "$1es");
  99. setPlural(/(bu)s$/i, "$1ses");
  100. setPlural(/(buffal|tomat)o$/i, "$1oes");
  101. setPlural(/([ti])a$/i, "$1a");
  102. setPlural(/([ti])um$/i, "$1a");
  103. setPlural(/sis$/i, "ses");
  104. setPlural(/ses$/i, "ses");
  105. setPlural(/(?:([^f])fe|([lr])f)$/i, "$1$2ves");
  106. setPlural(/(hive)$/i, "$1s");
  107. setPlural(/([^aeiouy]|qu)y$/i, "$1ies");
  108. setPlural(/(x|ch|ss|sh)$/i, "$1es");
  109. setPlural(/(matr|vert|ind)(?:ix|ex)$/i, "$1ices");
  110. setPlural(/([m|l])ouse$/i, "$1ice");
  111. setPlural(/([m|l])ice$/i, "$1ice");
  112. setPlural(/^(ox)$/i, "$1en");
  113. setPlural(/^(ox)en$/i, "$1en");
  114. setPlural(/(quiz)$/i, "$1zes");
  115. setSingular(/s$/i, "")
  116. setSingular(/ss$/i, "ss")
  117. setSingular(/(n)ews$/i, "$1ews")
  118. setSingular(/([ti])um$/i, "$1um")
  119. setSingular(/([ti])a$/i, "$1um")
  120. setSingular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, "$1$2sis")
  121. setSingular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)sis$/i, "$1$2sis")
  122. setSingular(/(^analy)ses$/i, "$1sis")
  123. setSingular(/(^analy)sis$/i, "$1sis")
  124. setSingular(/([^f])ves$/i, "$1fe")
  125. setSingular(/(hive)s$/i, "$1")
  126. setSingular(/(tive)s$/i, "$1")
  127. setSingular(/([lr])ves$/i, "$1f")
  128. setSingular(/([^aeiouy]|qu)ies$/i, "$1y")
  129. setSingular(/(s)eries$/i, "$1eries")
  130. setSingular(/(m)ovies$/i, "$1ovie")
  131. setSingular(/(x|ch|ss|sh)es$/i, "$1")
  132. setSingular(/([m|l])ice$/i, "$1ouse")
  133. setSingular(/([m|l])ouse$/i, "$1ouse")
  134. setSingular(/(bus)es$/i, "$1")
  135. setSingular(/(bus)$/i, "$1")
  136. setSingular(/(o)es$/i, "$1")
  137. setSingular(/(shoe)s$/i, "$1")
  138. setSingular(/(cris|ax|test)es$/i, "$1is")
  139. setSingular(/(cris|ax|test)is$/i, "$1is")
  140. setSingular(/(octop|vir)i$/i, "$1us")
  141. setSingular(/(octop|vir)us$/i, "$1us")
  142. setSingular(/(alias|status)es$/i, "$1")
  143. setSingular(/(alias|status)$/i, "$1")
  144. setSingular(/^(ox)en/i, "$1")
  145. setSingular(/(vert|ind)ices$/i, "$1ex")
  146. setSingular(/(matr)ices$/i, "$1ix")
  147. setSingular(/(quiz)zes$/i, "$1")
  148. setSingular(/(database)s$/i, "$1")
  149. setIrregular("person", "people");
  150. setIrregular("man", "men");
  151. setIrregular("child", "children");
  152. setIrregular("sex", "sexes");
  153. setIrregular("move", "moves");
  154. setIrregular("cow", "kine");
  155. setUncountable("equipment");
  156. setUncountable("information");
  157. setUncountable("rice");
  158. setUncountable("money");
  159. setUncountable("species");
  160. setUncountable("series");
  161. setUncountable("fish");
  162. setUncountable("sheep");
  163. setUncountable("jeans");
  164. })();
  165. /**
  166. @name inflection#parse
  167. @public
  168. @function
  169. @return {String} The inflection of the word from the type given
  170. @description Parse a word from the given inflection type
  171. @param {String} type A type of the inflection to use
  172. @param {String} word the word to parse
  173. */
  174. this.parse = function (type, word) {
  175. var lowWord = word.toLowerCase()
  176. , inflections = this.inflections[type];
  177. if (this.inflections.uncountables[lowWord]) {
  178. return word;
  179. }
  180. var i = -1;
  181. while (++i < inflections.length) {
  182. var rule = inflections[i][0]
  183. , replacement = inflections[i][1];
  184. if (rule.test(word)) {
  185. return word.replace(rule, replacement)
  186. }
  187. }
  188. return word;
  189. };
  190. /**
  191. @name inflection#pluralize
  192. @public
  193. @function
  194. @return {String} The plural inflection for the given word
  195. @description Create a plural inflection for a word
  196. @param {String} word the word to create a plural version for
  197. */
  198. this.pluralize = function (word) {
  199. return this.parse('plurals', word);
  200. };
  201. /**
  202. @name inflection#singularize
  203. @public
  204. @function
  205. @return {String} The singular inflection for the given word
  206. @description Create a singular inflection for a word
  207. @param {String} word the word to create a singular version for
  208. */
  209. this.singularize = function (word) {
  210. return this.parse('singulars', word);
  211. };
  212. })();
  213. module.exports = inflection;