mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-23 14:44:28 +00:00
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: https://origsvn.digium.com/svn/asterisk/trunk@168832 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -7174,7 +7174,102 @@ static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hebrew syntax */
|
/* 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)
|
static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
|
||||||
{
|
{
|
||||||
int res = 0;
|
int res = 0;
|
||||||
@@ -7857,78 +7952,6 @@ static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
|
|||||||
return res;
|
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 */
|
/* CHINESE (Taiwan) syntax */
|
||||||
static int vm_intro_tw(struct ast_channel *chan, struct vm_state *vms)
|
static int vm_intro_tw(struct ast_channel *chan, struct vm_state *vms)
|
||||||
{
|
{
|
||||||
@@ -7968,77 +7991,6 @@ static int vm_intro_tw(struct ast_channel *chan, struct vm_state *vms)
|
|||||||
return res;
|
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)
|
static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
|
||||||
{
|
{
|
||||||
char prefile[256];
|
char prefile[256];
|
||||||
@@ -8079,11 +8031,11 @@ static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm
|
|||||||
} else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
|
} else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
|
||||||
return vm_intro_no(chan, vms);
|
return vm_intro_no(chan, vms);
|
||||||
} else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
|
} 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 */
|
} else if (!strcasecmp(chan->language, "tw")) { /* CHINESE (Taiwan) syntax */
|
||||||
return vm_intro_tw(chan, vms);
|
return vm_intro_tw(chan, vms);
|
||||||
} else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
|
} 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 */
|
} else if (!strcasecmp(chan->language, "he")) { /* HEBREW syntax */
|
||||||
return vm_intro_he(chan, vms);
|
return vm_intro_he(chan, vms);
|
||||||
} else { /* Default to ENGLISH */
|
} else { /* Default to ENGLISH */
|
||||||
|
@@ -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);
|
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)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
109
main/say.c
109
main/say.c
@@ -50,6 +50,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|||||||
#include "asterisk/lock.h"
|
#include "asterisk/lock.h"
|
||||||
#include "asterisk/localtime.h"
|
#include "asterisk/localtime.h"
|
||||||
#include "asterisk/utils.h"
|
#include "asterisk/utils.h"
|
||||||
|
#include "asterisk/app.h"
|
||||||
|
|
||||||
/* Forward declaration */
|
/* Forward declaration */
|
||||||
static int wait_file(struct ast_channel *chan, const char *ints, const char *file, const char *lang);
|
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;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user