Improved logic for plural() and singular() as words like "tax" would become "taxs" and it was tough to code in new rules.
diff --git a/system/helpers/inflector_helper.php b/system/helpers/inflector_helper.php
index c7c113b..7b99bc5 100644
--- a/system/helpers/inflector_helper.php
+++ b/system/helpers/inflector_helper.php
@@ -41,30 +41,48 @@
 {
 	function singular($str)
 	{
-		$str = trim($str);
-		$end = substr($str, -3);
-        
-        $str = preg_replace('/(.*)?([s|c]h)es/i','$1$2',$str);
-        
-		if (strtolower($end) == 'ies')
-		{
-			$str = substr($str, 0, strlen($str)-3).(preg_match('/[a-z]/',$end) ? 'y' : 'Y');
-		}
-		elseif (strtolower($end) == 'ses')
-		{
-			$str = substr($str, 0, strlen($str)-2);
-		}
-		else
-		{
-			$end = strtolower(substr($str, -1));
+		$result = strval($str);
 
-			if ($end == 's')
+		$singular_rules = array(
+			'/(matr)ices$/'         => '\1ix',
+			'/(vert|ind)ices$/'     => '\1ex',
+			'/^(ox)en/'             => '\1',
+			'/(alias)es$/'          => '\1',
+			'/([octop|vir])i$/'     => '\1us',
+			'/(cris|ax|test)es$/'   => '\1is',
+			'/(shoe)s$/'            => '\1',
+			'/(o)es$/'              => '\1',
+			'/(bus|campus)es$/'     => '\1',
+			'/([m|l])ice$/'         => '\1ouse',
+			'/(x|ch|ss|sh)es$/'     => '\1',
+			'/(m)ovies$/'           => '\1\2ovie',
+			'/(s)eries$/'           => '\1\2eries',
+			'/([^aeiouy]|qu)ies$/'  => '\1y',
+			'/([lr])ves$/'          => '\1f',
+			'/(tive)s$/'            => '\1',
+			'/(hive)s$/'            => '\1',
+			'/([^f])ves$/'          => '\1fe',
+			'/(^analy)ses$/'        => '\1sis',
+			'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/' => '\1\2sis',
+			'/([ti])a$/'            => '\1um',
+			'/(p)eople$/'           => '\1\2erson',
+			'/(m)en$/'              => '\1an',
+			'/(s)tatuses$/'         => '\1\2tatus',
+			'/(c)hildren$/'         => '\1\2hild',
+			'/(n)ews$/'             => '\1\2ews',
+			'/([^u])s$/'            => '\1',
+		);
+		
+		foreach ($singular_rules as $rule => $replacement)
+		{
+			if (preg_match($rule, $result))
 			{
-				$str = substr($str, 0, strlen($str)-1);
+				$result = preg_replace($rule, $replacement, $result);
+				break;
 			}
 		}
 
-		return $str;
+		return $result;
 	}
 }
 
@@ -83,40 +101,41 @@
 if ( ! function_exists('plural'))
 {
 	function plural($str, $force = FALSE)
-	{   
-        $str = trim($str);
-		$end = substr($str, -1);
+	{
+		$result = strval($str);
+	
+		$plural_rules = array(
+			'/^(ox)$/'                 => '\1\2en',     // ox
+			'/([m|l])ouse$/'           => '\1ice',      // mouse, louse
+			'/(matr|vert|ind)ix|ex$/'  => '\1ices',     // matrix, vertex, index
+			'/(x|ch|ss|sh)$/'          => '\1es',       // search, switch, fix, box, process, address
+			'/([^aeiouy]|qu)y$/'       => '\1ies',      // query, ability, agency
+			'/(hive)$/'                => '\1s',        // archive, hive
+			'/(?:([^f])fe|([lr])f)$/'  => '\1\2ves',    // half, safe, wife
+			'/sis$/'                   => 'ses',        // basis, diagnosis
+			'/([ti])um$/'              => '\1a',        // datum, medium
+			'/(p)erson$/'              => '\1eople',    // person, salesperson
+			'/(m)an$/'                 => '\1en',       // man, woman, spokesman
+			'/(c)hild$/'               => '\1hildren',  // child
+			'/(buffal|tomat)o$/'       => '\1\2oes',    // buffalo, tomato
+			'/(bu|campu)s$/'           => '\1\2ses',    // bus, campus
+			'/(alias|status|virus)/'   => '\1es',       // alias
+			'/(octop)us$/'             => '\1i',        // octopus
+			'/(ax|cris|test)is$/'      => '\1es',       // axis, crisis
+			'/s$/'                     => 's',          // no change (compatibility)
+			'/$/'                      => 's',
+		);
 
-		if (preg_match('/y/i',$end))
+		foreach ($plural_rules as $rule => $replacement)
 		{
-			// Y preceded by vowel => regular plural
-			$vowels = array('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U');
-			$str = in_array(substr($str, -2, 1), $vowels) ? $str.'s' : substr($str, 0, -1).'ies';
-		}
-		elseif (preg_match('/h/i',$end))
-		{
-            if(preg_match('/^[c|s]h$/i',substr($str, -2)))
+			if (preg_match($rule, $result))
 			{
-				$str .= 'es';
+				$result = preg_replace($rule, $replacement, $result);
+				break;
 			}
-			else
-			{
-				$str .= 's';
-			}
-		}
-		elseif (preg_match('/s/i',$end))
-		{
-			if ($force == TRUE)
-			{
-				$str .= 'es';
-			}
-		}
-		else
-		{
-			$str .= 's';
 		}
 
-		return $str;
+		return $result;
 	}
 }