I was using Ajax to do some DOM manipulation in a Rails app I was working on. None of what I was doing needed to involve a round trip to server, the only reason I hit the server at all was to take advantage of Rails' (well, ActiveSupport's) pluralize function. Also, this technique required an extra action that I decided to eliminate as I try to move to a more RESTful architecture.
The only part of ActiveSupport's Inflector class I implemented are the ones I needed. I implemented even fewer of Rails' Inflector-based functions.
Stick all this in a file, such as "inflector.js".
1 /*
2 * This script depends on no outside libraries.
3 */
4
5 Inflector = {
6 /*
7 * The order of all these lists has been reversed from the way
8 * ActiveSupport had them to keep the correct priority.
9 */
10 Inflections: {
11 plural: [
12 [/(quiz)$/i, "$1zes" ],
13 [/^(ox)$/i, "$1en" ],
14 [/([m|l])ouse$/i, "$1ice" ],
15 [/(matr|vert|ind)ix|ex$/i, "$1ices" ],
16 [/(x|ch|ss|sh)$/i, "$1es" ],
17 [/([^aeiouy]|qu)y$/i, "$1ies" ],
18 [/(hive)$/i, "$1s" ],
19 [/(?:([^f])fe|([lr])f)$/i, "$1$2ves"],
20 [/sis$/i, "ses" ],
21 [/([ti])um$/i, "$1a" ],
22 [/(buffal|tomat)o$/i, "$1oes" ],
23 [/(bu)s$/i, "$1ses" ],
24 [/(alias|status)$/i, "$1es" ],
25 [/(octop|vir)us$/i, "$1i" ],
26 [/(ax|test)is$/i, "$1es" ],
27 [/s$/i, "s" ],
28 [/$/, "s" ]
29 ],
30 singular: [
31 [/(quiz)zes$/i, "$1" ],
32 [/(matr)ices$/i, "$1ix" ],
33 [/(vert|ind)ices$/i, "$1ex" ],
34 [/^(ox)en/i, "$1" ],
35 [/(alias|status)es$/i, "$1" ],
36 [/(octop|vir)i$/i, "$1us" ],
37 [/(cris|ax|test)es$/i, "$1is" ],
38 [/(shoe)s$/i, "$1" ],
39 [/(o)es$/i, "$1" ],
40 [/(bus)es$/i, "$1" ],
41 [/([m|l])ice$/i, "$1ouse" ],
42 [/(x|ch|ss|sh)es$/i, "$1" ],
43 [/(m)ovies$/i, "$1ovie" ],
44 [/(s)eries$/i, "$1eries"],
45 [/([^aeiouy]|qu)ies$/i, "$1y" ],
46 [/([lr])ves$/i, "$1f" ],
47 [/(tive)s$/i, "$1" ],
48 [/(hive)s$/i, "$1" ],
49 [/([^f])ves$/i, "$1fe" ],
50 [/(^analy)ses$/i, "$1sis" ],
51 [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, "$1$2sis"],
52 [/([ti])a$/i, "$1um" ],
53 [/(n)ews$/i, "$1ews" ],
54 [/s$/i, "" ]
55 ],
56 irregular: [
57 ['move', 'moves' ],
58 ['sex', 'sexes' ],
59 ['child', 'children'],
60 ['man', 'men' ],
61 ['person', 'people' ]
62 ],
63 uncountable: [
64 "sheep",
65 "fish",
66 "series",
67 "species",
68 "money",
69 "rice",
70 "information",
71 "equipment"
72 ]
73 },
74 ordinalize: function(number) {
75 if (11 <= parseInt(number) % 100 && parseInt(number) % 100 <= 13) {
76 return number + "th";
77 } else {
78 switch (parseInt(number) % 10) {
79 case 1: return number + "st";
80 case 2: return number + "nd";
81 case 3: return number + "rd";
82 default: return number + "th";
83 }
84 }
85 },
86 pluralize: function(word) {
87 for (var i = 0; i < Inflector.Inflections.uncountable.length; i++) {
88 var uncountable = Inflector.Inflections.uncountable[i];
89 if (word.toLowerCase == uncountable) {
90 return uncountable;
91 }
92 }
93 for (var i = 0; i < Inflector.Inflections.irregular.length; i++) {
94 var singular = Inflector.Inflections.irregular[i][0];
95 var plural = Inflector.Inflections.irregular[i][1];
96 if ((word.toLowerCase == singular) || (word == plural)) {
97 return plural;
98 }
99 }
100 for (var i = 0; i < Inflector.Inflections.plural.length; i++) {
101 var regex = Inflector.Inflections.plural[i][0];
102 var replace_string = Inflector.Inflections.plural[i][1];
103 if (regex.test(word)) {
104 return word.replace(regex, replace_string);
105 }
106 }
107 }
108 }
109
110 function ordinalize(number) {
111 return Inflector.ordinalize(number);
112 }
113
114 /*
115 * Javascript doesn't have optional parameters or overloading so I had to use
116 * the ever popular pseudo options hash object technique.
117 * required properties:
118 * count Number of objects to pluralize
119 * singular Singular noun for the objects
120 * optional property:
121 * plural Plural (probably irregular) noun for the objects
122 * examples:
123 * pluralize({ count: total_count, singular: "Issue" })
124 * pluralize({ count: total_count, singular: "Goose", plural: "Geese" })
125 */
126 function pluralize(options) {
127 return options.count + " " + (1 == parseInt(options.count) ?
128 options.singular :
129 options.plural || Inflector.pluralize(options.singular));
130 }