Index: apps/app_voicemail.c
===================================================================
--- apps/app_voicemail.c	(revision 126981)
+++ apps/app_voicemail.c	(working copy)
@@ -5700,72 +5700,50 @@
 	return res;
 }
 
-static int get_lastdigits(int num)
+/* Version of vm_intro() designed to work for many languages.
+ *
+ * If you use it with Russian, you will need these sound files:
+ *
+ * vm-newn		"new", singular, neuter
+ * vm-newx		"new", genative plural
+ * vm-message		"message" (singular form)
+ * vm-messagex1		"messages" (first counting plural form, genative singular)
+ * vm-messagex2		"messages" (second counting plural form, genative plural)
+ * digits/1n		"one" (neuter singular for phrases such as "one message" or "thirty one messages")
+ */
+static int vm_intro_universal(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
 {
-	num %= 100;
-	return (num < 20) ? num : num % 10;
-}
-
-static int vm_intro_ru(struct ast_channel *chan,struct vm_state *vms)
-{
 	int res;
 	int lastnum = 0;
-	int dcnum;
 
 	res = ast_play_and_wait(chan, "vm-youhave");
+
 	if (!res && vms->newmessages) {
-		lastnum = get_lastdigits(vms->newmessages);
-		dcnum = vms->newmessages - lastnum;
-		if (dcnum)
-			res = say_and_wait(chan, dcnum, chan->language);
-		if (!res && lastnum) {
-			if (lastnum == 1) 
-				res = ast_play_and_wait(chan, "digits/odno");
-			else
-				res = say_and_wait(chan, lastnum, chan->language);
-		}
+	    	lastnum = vms->newmessages;
 
+		res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender);
+
 		if (!res)
-			res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
+			res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
 
 		if (!res && vms->oldmessages)
 			res = ast_play_and_wait(chan, "vm-and");
 	}
 
 	if (!res && vms->oldmessages) {
-		lastnum = get_lastdigits(vms->oldmessages);
-		dcnum = vms->oldmessages - lastnum;
-		if (dcnum)
-			res = say_and_wait(chan, dcnum, chan->language);
-		if (!res && lastnum) {
-			if (lastnum == 1) 
-				res = ast_play_and_wait(chan, "digits/odno");
-			else
-				res = say_and_wait(chan, lastnum, chan->language);
-		}
+	    	lastnum = vms->oldmessages;
 
+		res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender);
+
 		if (!res)
-			res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
+			res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
 	}
 
-	if (!res && !vms->newmessages && !vms->oldmessages) {
-		lastnum = 0;
-		res = ast_play_and_wait(chan, "vm-no");
-	}
-
 	if (!res) {
-		switch (lastnum) {
-		case 1:
-			res = ast_play_and_wait(chan, "vm-soobshenie");
-			break;
-		case 2:
-		case 3:
-		case 4:
-			res = ast_play_and_wait(chan, "vm-soobsheniya");
-			break;
-		default:
-			res = ast_play_and_wait(chan, "vm-soobsheniy");
-			break;
+		if(lastnum == 0) {
+			res = ast_play_and_wait(chan, "vm-no");
+		} else {
+			res = ast_say_counted_noun(chan, lastnum, "vm-message");
 		}
 	}
 
@@ -5780,6 +5758,12 @@
  * vm-stare      'stare'
  */
 
+static int get_lastdigits(int num)
+{
+	num %= 100;
+	return (num < 20) ? num : num % 10;
+}
+
 static int vm_intro_ua(struct ast_channel *chan,struct vm_state *vms)
 {
 	int res;
@@ -5882,7 +5866,7 @@
 	} else if (!strcasecmp(chan->language, "no")) {	/* NORWEGIAN syntax */
 		return vm_intro_no(chan, vms);
 	} else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
-		return vm_intro_ru(chan, vms);
+		return vm_intro_universal(chan, vms, "n");
 	} else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
 		return vm_intro_ua(chan, vms);
 	} else {					/* Default to ENGLISH */
Index: include/asterisk/say.h
===================================================================
--- include/asterisk/say.h	(revision 126981)
+++ include/asterisk/say.h	(working copy)
@@ -151,6 +151,10 @@
 
 SAY_EXTERN int (* ast_say_date_with_format)(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *timezone) SAY_INIT(ast_say_date_with_format);
 
+SAY_EXTERN int (* ast_say_counted_noun)(struct ast_channel *chan, int num, const char *noun) SAY_INIT(ast_say_counted_noun);
+
+SAY_EXTERN int (* ast_say_counted_adjective)(struct ast_channel *chan, int num, const char *adjective, const char *gender) SAY_INIT(ast_say_counted_adjective);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
Index: main/say.c
===================================================================
--- main/say.c	(revision 126981)
+++ main/say.c	(working copy)
@@ -54,6 +54,7 @@
 #include "asterisk/lock.h"
 #include "asterisk/localtime.h"
 #include "asterisk/utils.h"
+#include "asterisk/app.h"
 
 /* Forward declaration */
 static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
@@ -6939,9 +6940,98 @@
 	return res;
 }
 
+/* In English, we use the plural for everything but one.  Yes, this code
+ * will make the plural of "man" "mans" as in "5 mans".  I think we can
+ * live with that.
+ */
+static const char *counted_noun_ending_en(int num)
+{
+	if(num == 1 || num == -1)
+		return "";
+	else
+		return "s";
+}
 
+/* In languages such as Russian and Ukrainian the rules are more complicated.
+ * There are two plural forms used in counting.  They are the genative 
+ * singular which we represent with the suffix x1 and the genative plural which
+ * we represent with the suffix x2.
+ */
+static const char *counted_noun_ending_ru(int num)
+{
+    	if(num < 0)
+	    num *= -1;
+	num %= 100;			/* never pay attention to more than two digits */
+	if (num >= 20)			/* for numbers 20 and above, pay attention to only last digit */
+	    num %= 10;
+	if (num == 1)			/* singular */
+	    return "";
+	if (num > 0 && num < 5)		/* genative singular */
+	    return "x1";
+	else				/* genative plural */
+	    return "x2";
+}
 
+static int say_counted_noun(struct ast_channel *chan, int num, const char noun[])
+{
+	char temp[32];
+	const char *ending;
+	if(!strcasecmp(chan->language, "ru"))
+		ending = counted_noun_ending_ru(num);
+	else if(!strcasecmp(chan->language, "ua"))
+		ending = counted_noun_ending_ru(num);
+	else
+		ending = counted_noun_ending_en(num);
+	snprintf(temp, sizeof(temp), "%s%s", noun, ending);
+	return ast_play_and_wait(chan, temp);
+}
+
+/* In English adjectives are not declined.  We say "one shiny pot",
+ * "15 shiny pots".
+ */
+static const char *counted_adjective_ending_en(int num)
+{
+	return "";
+}
+
 /*
+ * In slavic languages such as Russian and Ukrainian the rules for declining
+ * adjectives are simpler than those for nouns.  When counting we use only
+ * the singular (to which we give no suffix) and the genative plural (which
+ * we represent by adding an "x").  Oh, an in the singular gender matters
+ * so we append the supplied gender suffix ("m", "f", "n").
+ */
+static const char *counted_adjective_ending_ru(int num, const char gender[])
+{
+    	if(num < 0)
+	    num *= -1;
+	num %= 100;		/* never pay attention to more than two digits */
+	if (num >= 20)		/* at 20 and beyond only the last digit matters */
+	    num %= 10;
+	if(num == 1) {
+	    return gender ? gender : "";
+	} else {		/* all other numbers get the genative plural */
+	    return "x";
+	}
+}
+
+static int say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
+{
+	char temp[32];
+	const char *ending;
+	if(!strcasecmp(chan->language, "ru"))
+		ending = counted_adjective_ending_ru(num, gender);
+	else if(!strcasecmp(chan->language, "ua"))
+		ending = counted_adjective_ending_ru(num, gender);
+	else
+		ending = counted_adjective_ending_en(num);
+	snprintf(temp, sizeof(temp), "%s%s", adjective, ending);
+	return ast_play_and_wait(chan, temp);
+}
+
+
+
+/*
  * remap the 'say' functions to use those in this file
  */
 static void __attribute__((constructor)) __say_init(void)
@@ -6956,4 +7046,6 @@
 	ast_say_date = say_date;
 	ast_say_datetime_from_now = say_datetime_from_now;
 	ast_say_date_with_format = say_date_with_format;
+	ast_say_counted_noun = say_counted_noun;
+	ast_say_counted_adjective = say_counted_adjective;
 }
Index: main/pbx.c
===================================================================
--- main/pbx.c	(revision 126981)
+++ main/pbx.c	(working copy)
@@ -236,6 +236,8 @@
 static int pbx_builtin_saydigits(struct ast_channel *, void *);
 static int pbx_builtin_saycharacters(struct ast_channel *, void *);
 static int pbx_builtin_sayphonetic(struct ast_channel *, void *);
+static int pbx_builtin_saycountednoun(struct ast_channel *, void *);
+static int pbx_builtin_saycountedadj(struct ast_channel *, void *);
 int pbx_builtin_setvar(struct ast_channel *, void *);
 static int pbx_builtin_importvar(struct ast_channel *, void *);
 
@@ -430,6 +436,18 @@
 	"alphabet that correspond to the letters in the given string.\n"
 	},
 
+	{ "SayCountedNoun", pbx_builtin_saycountednoun,
+	"Say a noun in counting form",
+	"  SayCountedNoun(n,noun): This application will play the sound file containing\n"
+	"the proper form of a noun for a given number of the thing it indicates.\n"
+	},
+
+	{ "SayCountedAdj", pbx_builtin_saycountedadj,
+	"Say an adjective in counting form",
+	"  SayCountedAdj(n,adj,gender): This application will play the sound file containing\n"
+	"the proper form of an adjective for a given number of the thing it indicates.\n"
+	},
+
 	{ "SetAMAFlags", pbx_builtin_setamaflags,
 	"Set the AMA Flags",
 	"  SetAMAFlags([flag]): This application will set the channel's AMA Flags for\n"
@@ -6111,6 +6137,60 @@
 	return res;
 }
 
+static int pbx_builtin_saycountednoun(struct ast_channel *chan, void *data)
+{
+	char tmp[256];
+	char *ptr = tmp;
+	char *number;
+	char *noun;
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_WARNING, "SayCountedNoun requires two arguments\n");
+		return -1;
+	}
+
+	ast_copy_string(tmp, data, sizeof(tmp));
+	number = strsep(&ptr, "|");
+	noun = strsep(&ptr, "|");
+	if (!noun) {
+		ast_log(LOG_WARNING, "SayCountedNoun requires two arguments\n");
+		return -1;
+	}
+
+	return ast_say_counted_noun(chan, atoi(number), noun);
+}
+
+static int pbx_builtin_saycountedadj(struct ast_channel *chan, void *data)
+{
+	char tmp[256];
+	char *ptr = tmp;
+	char *number;
+	char *adjective;
+	char *options;
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_WARNING, "SayCountedAdj requires three arguments\n");
+		return -1;
+	}
+	ast_copy_string(tmp, data, sizeof(tmp));
+	number = strsep(&ptr, "|");
+	adjective = strsep(&ptr, "|");
+	if (!adjective) {
+		ast_log(LOG_WARNING, "SayCountedAdj requires two arguments\n");
+		return -1;
+	}
+	options = strsep(&number, "|");
+	if (options) {
+		if ( strcasecmp(options, "f") && strcasecmp(options,"m") &&
+			strcasecmp(options, "c") && strcasecmp(options, "n") ) {
+			ast_log(LOG_WARNING, "SayCountedAdj gender option is either 'f', 'm', 'c' or 'n'\n");
+			return -1;
+		}
+	}
+
+	return ast_say_counted_adjective(chan, atoi(number), adjective, options);
+}
+
 int load_pbx(void)
 {
 	int x;
