From 7cb9485d211a20ce530dc7848fb5743d76e1fc15 Mon Sep 17 00:00:00 2001 From: tilghman Date: Fri, 16 Jan 2009 18:53:48 +0000 Subject: Merged revisions 168832 via svnmerge from https://origsvn.digium.com/svn/asterisk/trunk ................ r168832 | tilghman | 2009-01-16 12:49:09 -0600 (Fri, 16 Jan 2009) | 13 lines Merged revisions 168828 via svnmerge from https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r168828 | tilghman | 2009-01-16 12:41:35 -0600 (Fri, 16 Jan 2009) | 6 lines 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.6.0@168835 f38db490-d61c-443f-a65b-d21fe96a405b --- apps/app_voicemail.c | 244 ++++++++++++++++++++----------------------------- include/asterisk/say.h | 4 + main/say.c | 109 ++++++++++++++++++++++ 3 files changed, 211 insertions(+), 146 deletions(-) diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index 91676937d..b302e01c1 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -6273,7 +6273,102 @@ static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms) res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 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 English syntax */ static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms) { @@ -6877,78 +6972,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; -} - /* CHINESE (Taiwan) syntax */ static int vm_intro_tw(struct ast_channel *chan, struct vm_state *vms) { @@ -6988,77 +7011,6 @@ static int vm_intro_tw(struct ast_channel *chan, struct vm_state *vms) 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]; @@ -7099,11 +7051,11 @@ 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, "tw")) { /* CHINESE (Taiwan) syntax */ return vm_intro_tw(chan, vms); } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */ - return vm_intro_ua(chan, vms); + return vm_intro_multilang(chan, vms, "n"); } else { /* Default to ENGLISH */ return vm_intro_en(chan, vms); } diff --git a/include/asterisk/say.h b/include/asterisk/say.h index 37098402c..613836106 100644 --- a/include/asterisk/say.h +++ b/include/asterisk/say.h @@ -163,6 +163,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 67b791ba4..32cef2954 100644 --- a/main/say.c +++ b/main/say.c @@ -50,6 +50,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); @@ -7754,6 +7755,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); +} + /* -- cgit v1.2.3