1 // based on the Inflector class from JavascriptMVC (http://javascriptmvc.com/index.html)
  2 // based on the Inflector class found on a DZone snippet contributed by Todd Sayre
  3 // http://snippets.dzone.com/posts/show/3205
  4 
  5 /*
  6 	This needs test coverage.
  7 	
  8 	I have found a few issues so far:
  9 	
 10 		"ax".singularize()
 11 		"mice".pluralize()
 12 
 13 		etc...
 14 		
 15 	I attempted to fix these by adding the "return word;" lines at the end of singularize and pluralize.
 16 */
 17 
 18 rio.Inflector = {
 19 	Inflections: {
 20 		plural: [
 21 			[/(quiz)$/i,               "$1zes"  ],
 22 			[/^(ox)$/i,                "$1en"   ],
 23 			[/([m|l])ouse$/i,          "$1ice"  ],
 24 			[/(matr|vert|ind)ix|ex$/i, "$1ices" ],
 25 			[/(x|ch|ss|sh)$/i,         "$1es"   ],
 26 			[/([^aeiouy]|qu)y$/i,      "$1ies"  ],
 27 			[/(hive)$/i,               "$1s"    ],
 28 			[/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
 29 			[/sis$/i,                  "ses"    ],
 30 			[/([ti])um$/i,             "$1a"    ],
 31 			[/(buffal|tomat)o$/i,      "$1oes"  ],
 32 			[/(bu)s$/i,                "$1ses"  ],
 33 			[/(alias|status)$/i,       "$1es"   ],
 34 			[/(octop|vir)us$/i,        "$1i"    ],
 35 			[/(ax|test)is$/i,          "$1es"   ],
 36 			[/s$/i,                    "s"      ],
 37 			[/$/,                      "s"      ]
 38 		],
 39 		singular: [
 40 			[/(quiz)zes$/i,                                                    "$1"     ],
 41 			[/(matr)ices$/i,                                                   "$1ix"   ],
 42 			[/(vert|ind)ices$/i,                                               "$1ex"   ],
 43 			[/^(ox)en/i,                                                       "$1"     ],
 44 			[/(alias|status)es$/i,                                             "$1"     ],
 45 			[/(octop|vir)i$/i,                                                 "$1us"   ],
 46 			[/(cris|ax|test)es$/i,                                             "$1is"   ],
 47 			[/(shoe)s$/i,                                                      "$1"     ],
 48 			[/(o)es$/i,                                                        "$1"     ],
 49 			[/(bus)es$/i,                                                      "$1"     ],
 50 			[/([m|l])ice$/i,                                                   "$1ouse" ],
 51 			[/(x|ch|ss|sh)es$/i,                                               "$1"     ],
 52 			[/(m)ovies$/i,                                                     "$1ovie" ],
 53 			[/(s)eries$/i,                                                     "$1eries"],
 54 			[/([^aeiouy]|qu)ies$/i,                                            "$1y"    ],
 55 			[/([lr])ves$/i,                                                    "$1f"    ],
 56 			[/(tive)s$/i,                                                      "$1"     ],
 57 			[/(hive)s$/i,                                                      "$1"     ],
 58 			[/([^f])ves$/i,                                                    "$1fe"   ],
 59 			[/(^analy)ses$/i,                                                  "$1sis"  ],
 60 			[/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, "$1$2sis"],
 61 			[/([ti])a$/i,                                                      "$1um"   ],
 62 			[/(n)ews$/i,                                                       "$1ews"  ],
 63 			[/s$/i,                                                            ""       ]
 64 		],
 65 		irregular: [
 66 			['move',   'moves'   ],
 67 			['sex',    'sexes'   ],
 68 			['child',  'children'],
 69 			['man',    'men'     ],
 70 			['foreman', 'foremen'],
 71 			['person', 'people'  ]
 72 		],
 73 		uncountable: [
 74 			"sheep",
 75 			"fish",
 76 			"series",
 77 			"species",
 78 			"money",
 79 			"rice",
 80 			"information",
 81 			"equipment"
 82 		]
 83 	},
 84 	pluralize: function(word) {
 85 		var i;
 86 		for (i = 0; i < this.Inflections.uncountable.length; i++) {
 87 			var uncountable = this.Inflections.uncountable[i];
 88 			if (word.toLowerCase() == uncountable) {
 89 				return uncountable;
 90 			}
 91 		}
 92 		for (i = 0; i < this.Inflections.irregular.length; i++) {
 93 			var singular = this.Inflections.irregular[i][0];
 94 			var plural   = this.Inflections.irregular[i][1];
 95 			if ((word.toLowerCase() == singular) || (word == plural)) {
 96 				return word.substring(0,1)+plural.substring(1);
 97 			}
 98 		}
 99 		for (i = 0; i < this.Inflections.plural.length; i++) {
100 			var regex          = this.Inflections.plural[i][0];
101 			var replaceString = this.Inflections.plural[i][1];
102 			if (regex.test(word)) {
103 				return word.replace(regex, replaceString);
104 			}
105 		}
106 		
107 		return word;
108 	},
109 	singularize: function(word) {
110 		var i;
111 		for (i = 0; i < this.Inflections.uncountable.length; i++) {
112 			var uncountable = this.Inflections.uncountable[i];
113 			if (word.toLowerCase() == uncountable) {
114 				return uncountable;
115 			}
116 		}
117 		for (i = 0; i < this.Inflections.irregular.length; i++) {
118 			var singular = this.Inflections.irregular[i][0];
119 			var plural   = this.Inflections.irregular[i][1];
120 			if ((word.toLowerCase() == singular) || (word.toLowerCase() == plural)) {
121 				return word.substring(0,1)+singular.substring(1);
122 			}
123 		}
124 		for (i = 0; i < this.Inflections.singular.length; i++) {
125 			var regex          = this.Inflections.singular[i][0];
126 			var replaceString = this.Inflections.singular[i][1];
127 			if (regex.test(word)) {
128 				return word.replace(regex, replaceString);
129 			}
130 		}
131 		
132 		return word;
133 	}
134 };
135 
136 
137 Object.extend(String.prototype, {
138 	pluralize: function(count, plural) {
139 		if (typeof count == 'undefined') {
140 			return rio.Inflector.pluralize(this);
141 		} else {
142 			return count + ' ' + (1 == parseInt(count, 10) ? this : plural || rio.Inflector.pluralize(this));
143 		}
144 	},
145 
146 	singularize: function(count) {
147 		if (count == undefined) {
148 			return rio.Inflector.singularize(this);
149 		} else {
150 			return count + " " + rio.Inflector.singularize(this);
151 		}
152 	},
153 
154 	isSingular: function(){
155 		if(this.singularize() == this) {
156 			return true;
157 		}
158 		return false;
159 	}
160 });