aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2009-01-16 18:41:35 +0000
committertilghman <tilghman@f38db490-d61c-443f-a65b-d21fe96a405b>2009-01-16 18:41:35 +0000
commit04a98dd615ed71827af941ccca2617f5d4b36032 (patch)
treeafaec45f1404ba87888045afdf3cdcaa459fcb25
parentcaebf8461f9849f484eb5bbd649880e457c20e31 (diff)
Fix the conjugation of Russian and Ukrainian languages.
(related to issue #12475) Reported by: chappell Patches: vm_multilang.patch uploaded by chappell (license 8) git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.4@168828 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--apps/app_voicemail.c246
-rw-r--r--include/asterisk/say.h4
-rw-r--r--main/say.c109
3 files changed, 211 insertions, 148 deletions
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index b0530f78c..f8dfac37c 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -5816,7 +5816,7 @@ static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
return res;
}
-
+
/* Default English syntax */
static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
{
@@ -5861,6 +5861,101 @@ static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
return res;
}
+/* Version of vm_intro() designed to work for many languages.
+ *
+ * It is hoped that this function can prevent the proliferation of
+ * language-specific vm_intro() functions and in time replace the language-
+ * specific functions which already exist. An examination of the language-
+ * specific functions revealed that they all corrected the same deficiencies
+ * in vm_intro_en() (which was the default function). Namely:
+ *
+ * 1) The vm-Old and vm-INBOX sound files were overloaded. The English
+ * wording of the voicemail greeting hides this problem. For example,
+ * vm-INBOX contains only the word "new". This means that both of these
+ * sequences produce valid utterances:
+ * * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
+ * * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
+ * However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
+ * in many languages) the first utterance becomes "you have 1 the new message".
+ * 2) The function contains hardcoded rules for pluralizing the word "message".
+ * These rules are correct for English, but not for many other languages.
+ * 3) No attempt is made to pluralize the adjectives ("old" and "new") as
+ * required in many languages.
+ * 4) The gender of the word for "message" is not specified. This is a problem
+ * because in many languages the gender of the number in phrases such
+ * as "you have one new message" must match the gender of the word
+ * meaning "message".
+ *
+ * Fixing these problems for each new language has meant duplication of effort.
+ * This new function solves the problems in the following general ways:
+ * 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
+ * and vm-Old respectively for those languages where it makes sense.
+ * 2) Call ast_say_counted_noun() to put the proper gender and number prefix
+ * on vm-message.
+ * 3) Call ast_say_counted_adjective() to put the proper gender and number
+ * prefix on vm-new and vm-old (none for English).
+ * 4) Pass the gender of the language's word for "message" as an agument to
+ * this function which is can in turn pass on to the functions which
+ * say numbers and put endings on nounds and adjectives.
+ *
+ * All languages require these messages:
+ * vm-youhave "You have..."
+ * vm-and "and"
+ * vm-no "no" (in the sense of "none", as in "you have no messages")
+ *
+ * To use it for English, you will need these additional sound files:
+ * vm-new "new"
+ * vm-message "message", singular
+ * vm-messages "messages", plural
+ *
+ * If you use it for Russian and other slavic languages, you will need these additional sound files:
+ *
+ * vm-newn "novoye" (singular, neuter)
+ * vm-newx "novikh" (counting plural form, genative plural)
+ * vm-message "sobsheniye" (singular form)
+ * vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
+ * vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
+ * digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
+ * digits/2n "dva" (neuter singular)
+ */
+static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
+{
+ int res;
+ int lastnum = 0;
+
+ res = ast_play_and_wait(chan, "vm-youhave");
+
+ if (!res && vms->newmessages) {
+ lastnum = vms->newmessages;
+
+ if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
+ 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 = vms->oldmessages;
+
+ if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
+ res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
+ }
+ }
+
+ if (!res) {
+ if (lastnum == 0) {
+ res = ast_play_and_wait(chan, "vm-no");
+ } else {
+ res = ast_say_counted_noun(chan, lastnum, "vm-message");
+ }
+ }
+
+ return res;
+}
+
/* Default Hebrew syntax */
static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
{
@@ -6491,151 +6586,6 @@ static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
return res;
}
-static int get_lastdigits(int num)
-{
- 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);
- }
-
- if (!res)
- res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
-
- 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);
- }
-
- if (!res)
- res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
- }
-
- 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;
- }
- }
-
- return res;
-}
-
-/* UKRAINIAN syntax */
-/* in ukrainian the syntax is different so we need the following files
- * --------------------------------------------------------
- * /digits/ua/1e 'odne'
- * vm-nove 'nove'
- * vm-stare 'stare'
- */
-
-static int vm_intro_ua(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/ua/1e");
- else
- res = say_and_wait(chan, lastnum, chan->language);
- }
-
- if (!res)
- res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-nove" : "vm-INBOX");
-
- 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/ua/1e");
- else
- res = say_and_wait(chan, lastnum, chan->language);
- }
-
- if (!res)
- res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-stare" : "vm-Old");
- }
-
- if (!res && !vms->newmessages && !vms->oldmessages) {
- lastnum = 0;
- res = ast_play_and_wait(chan, "vm-no");
- }
-
- if (!res) {
- switch (lastnum) {
- case 1:
- case 2:
- case 3:
- case 4:
- res = ast_play_and_wait(chan, "vm-message");
- break;
- default:
- res = ast_play_and_wait(chan, "vm-messages");
- break;
- }
- }
-
- return res;
-}
-
-
static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
{
char prefile[256];
@@ -6675,9 +6625,9 @@ static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm
} 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_multilang(chan, vms, "n");
} else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
- return vm_intro_ua(chan, vms);
+ return vm_intro_multilang(chan, vms, "n");
} else if (!strcasecmp(chan->language, "he")) { /* HEBREW syntax */
return vm_intro_he(chan, vms);
} else { /* Default to ENGLISH */
diff --git a/include/asterisk/say.h b/include/asterisk/say.h
index 17070c2c4..bc36a2a61 100644
--- a/include/asterisk/say.h
+++ b/include/asterisk/say.h
@@ -151,6 +151,10 @@ SAY_EXTERN int (* ast_say_datetime_from_now)(struct ast_channel *chan, time_t t,
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);
+int ast_say_counted_noun(struct ast_channel *chan, int num, const char *noun);
+
+int ast_say_counted_adjective(struct ast_channel *chan, int num, const char *adjective, const char *gender);
+
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
diff --git a/main/say.c b/main/say.c
index 5903b4597..b6b973ccd 100644
--- a/main/say.c
+++ b/main/say.c
@@ -54,6 +54,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#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);
@@ -7265,6 +7266,114 @@ static int ast_say_datetime_from_now_ge(struct ast_channel *chan, time_t t, cons
return res;
}
+/* In English, we use the plural for everything but one. For example:
+ * 1 degree
+ * 2 degrees
+ * 5 degrees
+ * The filename for the plural form is generated by appending "s". Note that
+ * purpose is to generate a unique filename, not to implement irregular
+ * declensions. Thus:
+ * 1 man
+ * 2 mans (the "mans" soundfile will of course say "men")
+ */
+static const char *counted_noun_ending_en(int num)
+{
+ if (num == 1 || num == -1) {
+ return "";
+ } else {
+ return "s";
+ }
+}
+
+/* Counting of objects in slavic 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". The base names
+ * of the soundfiles remain in English. For example:
+ * 1 degree (soudfile says "gradus")
+ * 2 degreex1 (soundfile says "gradusa")
+ * 5 degreex2 (soundfile says "gradusov")
+ */
+static const char *counted_noun_ending_slavic(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) { /* 2--5 get genative singular */
+ return "x1";
+ } else { /* 5--19 get genative plural */
+ return "x2";
+ }
+}
+
+int ast_say_counted_noun(struct ast_channel *chan, int num, const char noun[])
+{
+ char *temp;
+ int temp_len;
+ const char *ending;
+ if (!strcasecmp(chan->language, "ru")) { /* Russian */
+ ending = counted_noun_ending_slavic(num);
+ } else if(!strcasecmp(chan->language, "ua")) { /* Ukrainian */
+ ending = counted_noun_ending_slavic(num);
+ } else if(!strcasecmp(chan->language, "ua")) { /* Polish */
+ ending = counted_noun_ending_slavic(num);
+ } else { /* English and default */
+ ending = counted_noun_ending_en(num);
+ }
+ temp = alloca((temp_len = (strlen(noun) + strlen(ending) + 1)));
+ snprintf(temp, temp_len, "%s%s", noun, ending);
+ return ast_play_and_wait(chan, temp);
+}
+
+/*
+ * 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";
+ }
+}
+
+int ast_say_counted_adjective(struct ast_channel *chan, int num, const char adjective[], const char gender[])
+{
+ char *temp;
+ int temp_len;
+ const char *ending;
+ if (!strcasecmp(chan->language, "ru")) { /* Russian */
+ ending = counted_adjective_ending_ru(num, gender);
+ } else if (!strcasecmp(chan->language, "ua")) { /* Ukrainian */
+ ending = counted_adjective_ending_ru(num, gender);
+ } else if (!strcasecmp(chan->language, "pl")) { /* Polish */
+ ending = counted_adjective_ending_ru(num, gender);
+ } else { /* English and default */
+ ending = "";
+ }
+ temp = alloca((temp_len = (strlen(adjective) + strlen(ending) + 1)));
+ snprintf(temp, temp_len, "%s%s", adjective, ending);
+ return ast_play_and_wait(chan, temp);
+}
+
/*