From: Talha Asghar Date: Thu, 22 Jan 2026 15:33:12 +0000 (+0000) Subject: say.c: added language support for pashto and dari X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1d5ebbf4a93c7facee0fa1158cc69a44e227aa78;p=thirdparty%2Fasterisk.git say.c: added language support for pashto and dari With this new feature, users who speak these languages can now benefit from the text-to-speech functionality provided by asterisk. This will make the platform more accessible and useful to a wider range of users, particularly those in regions where Pashto and Dari are spoken. This contribution will help to improve the overall usability and inclusivity of the asterisk platform. Fixes: #1724 --- diff --git a/main/say.c b/main/say.c index 6b279f0fe5..cfa7ed4bfe 100644 --- a/main/say.c +++ b/main/say.c @@ -762,6 +762,8 @@ static int ast_say_number_full_en(struct ast_channel *chan, int num, const char static int ast_say_number_full_cs(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); static int ast_say_number_full_da(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); static int ast_say_number_full_de(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); +static int ast_say_number_full_ps(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); +static int ast_say_number_full_dr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd); static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd); static int ast_say_number_full_es(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); static int ast_say_number_full_fr(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); @@ -790,6 +792,8 @@ static int ast_say_enumeration_full_de(struct ast_channel *chan, int num, const static int ast_say_enumeration_full_he(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); static int ast_say_enumeration_full_is(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd); static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd); +static int ast_say_enumeration_full_ps(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd); +static int ast_say_enumeration_full_dr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd); /* Forward declarations of ast_say_date, ast_say_datetime and ast_say_time functions */ static int ast_say_date_en(struct ast_channel *chan, time_t t, const char *ints, const char *lang); @@ -922,6 +926,10 @@ static int say_number_full(struct ast_channel *chan, int num, const char *ints, return ast_say_number_full_ur(chan, num, ints, language, options, audiofd, ctrlfd); } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */ return ast_say_number_full_vi(chan, num, ints, language, audiofd, ctrlfd); + } else if (!strncasecmp(language, "dr", 2)) { /* Dari syntax */ + return ast_say_number_full_dr(chan, num, ints, language, audiofd, ctrlfd); + } else if (!strncasecmp(language, "ps", 2)) { /* Pashto syntax */ + return ast_say_number_full_ps(chan, num, ints, language, options, audiofd, ctrlfd); } /* Default to english */ @@ -1315,6 +1323,151 @@ static int ast_say_number_full_de(struct ast_channel *chan, int num, const char return res; } +/*! \brief ast_say_number_full_ps: Pashto syntax + + New files: + In addition to English, the following sounds are required: + - wesht + - sawa + - zara + */ +static int ast_say_number_full_ps(struct ast_channel *chan, int num, const char *ints, const char *language, const char *options, int audiofd, int ctrlfd) +{ + int res = 0, t = 0; + char fn[256] = ""; + char fna[256] = ""; + int playWesht = 0; + if (!num){ + return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd); + } + + while (!res && num) { + /* The grammar for Pashto numbers is the same as for English except + * for the following: + * - numbers 20 through 99 are said in reverse order, i.e. 21 is + * "one-and twenty" and 68 is "eight-and sixty". + * - 20 has unique pronounciation if there is a unit number after it + * i.e. 21 is one wesht while 20 is just "twenty" + * - 200 is called sawa + * - 2000 is called zara + */ + if (num < 0) { + ast_copy_string(fn, "digits/minus", sizeof(fn)); + if ( num > INT_MIN ) { + num = -num; + } else { + num = 0; + } + } else if (num < 20) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else if (num < 100) { + int ones = num % 10; + if (ones) { + snprintf(fn, sizeof(fn), "digits/%d", ones); + num -= ones; + if (num == 20){ + playWesht = 1; + } + } else { + if (num == 20 && playWesht == 1){ + snprintf(fn, sizeof(fn), "digits/wesht"); + playWesht = 0; + } else{ + snprintf(fn, sizeof(fn), "digits/%d", num); + } + num = 0; + } + } else if (num == 100 && t == 0) { + ast_copy_string(fn, "digits/hundred", sizeof(fn)); + num = 0; + } else if (num < 1000) { + int hundreds = num / 100; + num = num % 100; + /* 100: hundreds = 1, num = 0 + * 101: hundreds = 1, num = 1 + * 200: hundreds = 2, num = 0 + * 202: hundreds = 2, num = 2 + */ + if (hundreds == 1){ + if (num){ + snprintf(fn, sizeof(fn), "digits/%d", hundreds); + } + ast_copy_string(fna, "digits/hundred", sizeof(fna)); + } else{ + snprintf(fn, sizeof(fn), "digits/%d", hundreds); + ast_copy_string(fna, "digits/sawa", sizeof(fna)); + } + } else if (num == 1000 && t == 0) { + ast_copy_string(fn, "digits/thousand", sizeof(fn)); + num = 0; + } else if (num < 1000000) { + int thousands = num / 1000; + num = num % 1000; + t = 1; + if (thousands == 1) { + if (num){ + snprintf(fn, sizeof(fn), "digits/%d", thousands); + } + ast_copy_string(fna, "digits/thousand", sizeof(fna)); + } else { + res = ast_say_number_full_ps(chan, thousands, ints, language, options, audiofd, ctrlfd); + if (res) + return res; + ast_copy_string(fn, "digits/zara", sizeof(fn)); + } + } else if (num < 1000000000) { + int millions = num / 1000000; + num = num % 1000000; + t = 1; + if (millions == 1) { + ast_copy_string(fna, "digits/million", sizeof(fna)); + } else { + res = ast_say_number_full_ps(chan, millions, ints, language, options, audiofd, ctrlfd); + if (res) + return res; + ast_copy_string(fn, "digits/millions", sizeof(fn)); + } + } else if (num <= INT_MAX) { + int billions = num / 1000000000; + num = num % 1000000000; + t = 1; + if (billions == 1) { + ast_copy_string(fna, "digits/milliard", sizeof(fna)); + } else { + res = ast_say_number_full_ps(chan, billions, ints, language, options, audiofd, ctrlfd); + if (res) { + return res; + } + ast_copy_string(fn, "digits/milliards", sizeof(fn)); + } + } else { + ast_debug(1, "Number '%d' is too big for me\n", num); + res = -1; + } + if (!res) { + if (!ast_streamfile(chan, fn, language)) { + if ((audiofd > -1) && (ctrlfd > -1)) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + else + res = ast_waitstream(chan, ints); + } + ast_stopstream(chan); + if (!res) { + if (strlen(fna) != 0 && !ast_streamfile(chan, fna, language)) { + if ((audiofd > -1) && (ctrlfd > -1)) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + else + res = ast_waitstream(chan, ints); + } + ast_stopstream(chan); + strcpy(fna, ""); + } + } + } + return res; +} + /*! \brief ast_say_number_full_en_GB: British syntax New files: - In addition to American English, the following sounds are required: "vm-and" @@ -1391,6 +1544,85 @@ static int ast_say_number_full_en_GB(struct ast_channel *chan, int num, const ch return res; } +/*! \brief ast_say_number_full_en_dr: Dari syntax + New files: + - In addition to American English, the following sounds are required: "o" + */ +static int ast_say_number_full_dr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd) +{ + int res = 0; + int playh = 0; + int playa = 0; + int say_o = 0; + char fn[256] = ""; + if (!num) + return ast_say_digits_full(chan, 0, ints, language, audiofd, ctrlfd); + + while (!res && (num || playh || playa )) { + if (num < 0) { + ast_copy_string(fn, "digits/minus", sizeof(fn)); + if ( num > INT_MIN ) { + num = -num; + } else { + num = 0; + } + } else if (playh) { + ast_copy_string(fn, "digits/hundred", sizeof(fn)); + playh = 0; + } else if (playa) { + ast_copy_string(fn, "digits/o", sizeof(fn)); + playa = 0; + } else if (num < 20) { + snprintf(fn, sizeof(fn), "digits/%d", num); + num = 0; + } else if (num < 100) { + snprintf(fn, sizeof(fn), "digits/%d", (num /10) * 10); + num %= 10; + if (num && say_o) + playa++; + } else if (num < 1000) { + int hundreds = num / 100; + snprintf(fn, sizeof(fn), "digits/%d", (num / 100)); + + playh++; + num -= 100 * hundreds; + if (num && say_o) + playa++; + } else if (num < 1000000) { + res = ast_say_number_full_dr(chan, num / 1000, ints, language, audiofd, ctrlfd); + if (res) + return res; + ast_copy_string(fn, "digits/thousand", sizeof(fn)); + num %= 1000; + if (num && say_o) + playa++; + } else if (num < 1000000000) { + int millions = num / 1000000; + res = ast_say_number_full_dr(chan, millions, ints, language, audiofd, ctrlfd); + if (res) + return res; + ast_copy_string(fn, "digits/million", sizeof(fn)); + num %= 1000000; + if (num && num < 100 && say_o) + playa++; + } else { + ast_debug(1, "Number '%d' is too big for me\n", num); + res = -1; + } + + if (!res) { + if (!ast_streamfile(chan, fn, language)) { + if ((audiofd > -1) && (ctrlfd > -1)) + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + else + res = ast_waitstream(chan, ints); + } + ast_stopstream(chan); + } + } + return res; +} + /*! \brief ast_say_number_full_es: Spanish syntax New files: @@ -3240,6 +3472,10 @@ static int say_enumeration_full(struct ast_channel *chan, int num, const char *i return ast_say_enumeration_full_is(chan, num, ints, language, options, audiofd, ctrlfd); } else if (!strncasecmp(language, "vi", 2)) { /* Vietnamese syntax */ return ast_say_enumeration_full_vi(chan, num, ints, language, audiofd, ctrlfd); + } else if (!strncasecmp(language, "ps", 2)) { /* Pashto syntax */ + return ast_say_enumeration_full_ps(chan, num, ints, language, audiofd, ctrlfd); + } else if (!strncasecmp(language, "dr", 2)) { /* Dari syntax */ + return ast_say_enumeration_full_dr(chan, num, ints, language, audiofd, ctrlfd); } /* Default to english */ @@ -3345,6 +3581,204 @@ static int ast_say_enumeration_full_en(struct ast_channel *chan, int num, const return res; } +/*! \brief ast_say_enumeration_full_ps: Pashto syntax + \note This is the default syntax, if no other syntax defined in this file is used */ +static int ast_say_enumeration_full_ps(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd) +{ + int res = 0, t = 0; + char fn[256] = ""; + + while (!res && num) { + if (num < 0) { + ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */ + if ( num > INT_MIN ) { + num = -num; + } else { + num = 0; + } + } else if (num <= 31) { + snprintf(fn, sizeof(fn), "digits/h-%d", num); + num = 0; + } else if (num < 100) { + int tens = num / 10; + num = num % 10; + if (num == 0) { + snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10)); + } else { + snprintf(fn, sizeof(fn), "digits/%d", (tens * 10)); + } + } else if (num < 1000) { + int hundreds = num / 100; + num = num % 100; + if (hundreds > 1 || t == 1) { + res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd); + } + if (res) + return res; + if (num) { + ast_copy_string(fn, "digits/hundred", sizeof(fn)); + } else { + ast_copy_string(fn, "digits/h-hundred", sizeof(fn)); + } + } else if (num < 1000000) { + int thousands = num / 1000; + num = num % 1000; + if (thousands > 1 || t == 1) { + res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd); + } + if (res) + return res; + if (num) { + ast_copy_string(fn, "digits/thousand", sizeof(fn)); + } else { + ast_copy_string(fn, "digits/h-thousand", sizeof(fn)); + } + t = 1; + } else if (num < 1000000000) { + int millions = num / 1000000; + num = num % 1000000; + t = 1; + res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd); + if (res) + return res; + if (num) { + ast_copy_string(fn, "digits/million", sizeof(fn)); + } else { + ast_copy_string(fn, "digits/h-million", sizeof(fn)); + } + } else if (num < INT_MAX) { + int billions = num / 1000000000; + num = num % 1000000000; + t = 1; + res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd); + if (res) + return res; + if (num) { + ast_copy_string(fn, "digits/billion", sizeof(fn)); + } else { + ast_copy_string(fn, "digits/h-billion", sizeof(fn)); + } + } else if (num == INT_MAX) { + ast_copy_string(fn, "digits/h-last", sizeof(fn)); + num = 0; + } else { + ast_debug(1, "Number '%d' is too big for me\n", num); + res = -1; + } + + if (!res) { + if (!ast_streamfile(chan, fn, language)) { + if ((audiofd > -1) && (ctrlfd > -1)) { + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + } else { + res = ast_waitstream(chan, ints); + } + } + ast_stopstream(chan); + } + } + return res; +} + +/*! \brief ast_say_enumeration_full_dr: Dari syntax + \note This is the default syntax, if no other syntax defined in this file is used */ +static int ast_say_enumeration_full_dr(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd) +{ + int res = 0, t = 0; + char fn[256] = ""; + + while (!res && num) { + if (num < 0) { + ast_copy_string(fn, "digits/minus", sizeof(fn)); /* kind of senseless for enumerations, but our best effort for error checking */ + if ( num > INT_MIN ) { + num = -num; + } else { + num = 0; + } + } else if (num <= 31) { + snprintf(fn, sizeof(fn), "digits/h-%d", num); + num = 0; + } else if (num < 100) { + int tens = num / 10; + num = num % 10; + if (num == 0) { + snprintf(fn, sizeof(fn), "digits/h-%d", (tens * 10)); + } else { + snprintf(fn, sizeof(fn), "digits/%d", (tens * 10)); + } + } else if (num < 1000) { + int hundreds = num / 100; + num = num % 100; + if (hundreds > 1 || t == 1) { + res = ast_say_number_full_en(chan, hundreds, ints, language, audiofd, ctrlfd); + } + if (res) + return res; + if (num) { + ast_copy_string(fn, "digits/hundred", sizeof(fn)); + } else { + ast_copy_string(fn, "digits/h-hundred", sizeof(fn)); + } + } else if (num < 1000000) { + int thousands = num / 1000; + num = num % 1000; + if (thousands > 1 || t == 1) { + res = ast_say_number_full_en(chan, thousands, ints, language, audiofd, ctrlfd); + } + if (res) + return res; + if (num) { + ast_copy_string(fn, "digits/thousand", sizeof(fn)); + } else { + ast_copy_string(fn, "digits/h-thousand", sizeof(fn)); + } + t = 1; + } else if (num < 1000000000) { + int millions = num / 1000000; + num = num % 1000000; + t = 1; + res = ast_say_number_full_en(chan, millions, ints, language, audiofd, ctrlfd); + if (res) + return res; + if (num) { + ast_copy_string(fn, "digits/million", sizeof(fn)); + } else { + ast_copy_string(fn, "digits/h-million", sizeof(fn)); + } + } else if (num < INT_MAX) { + int billions = num / 1000000000; + num = num % 1000000000; + t = 1; + res = ast_say_number_full_en(chan, billions, ints, language, audiofd, ctrlfd); + if (res) + return res; + if (num) { + ast_copy_string(fn, "digits/billion", sizeof(fn)); + } else { + ast_copy_string(fn, "digits/h-billion", sizeof(fn)); + } + } else if (num == INT_MAX) { + ast_copy_string(fn, "digits/h-last", sizeof(fn)); + num = 0; + } else { + ast_debug(1, "Number '%d' is too big for me\n", num); + res = -1; + } + + if (!res) { + if (!ast_streamfile(chan, fn, language)) { + if ((audiofd > -1) && (ctrlfd > -1)) { + res = ast_waitstream_full(chan, ints, audiofd, ctrlfd); + } else { + res = ast_waitstream(chan, ints); + } + } + ast_stopstream(chan); + } + } + return res; +} + static int ast_say_enumeration_full_vi(struct ast_channel *chan, int num, const char *ints, const char *language, int audiofd, int ctrlfd) { int res = 0; @@ -3982,6 +4416,10 @@ static int say_date(struct ast_channel *chan, time_t t, const char *ints, const return ast_say_date_pt(chan, t, ints, lang); } else if (!strncasecmp(lang, "th", 2)) { /* Thai syntax */ return ast_say_date_th(chan, t, ints, lang); + } else if (!strncasecmp(lang, "ps", 2)) { /* Pashto syntax */ + return ast_say_date_en(chan, t, ints, lang); + } else if (!strncasecmp(lang, "dr", 2)) { /* Dari syntax */ + return ast_say_date_en(chan, t, ints, lang); } /* Default to English */ @@ -7253,6 +7691,10 @@ static int say_time(struct ast_channel *chan, time_t t, const char *ints, const return(ast_say_time_th(chan, t, ints, lang)); } else if (!strncasecmp(lang, "zh", 2)) { /* Taiwanese / Chinese syntax */ return ast_say_time_zh(chan, t, ints, lang); + } else if (!strncasecmp(lang, "ps", 2)) { /* Pashto syntax */ + return ast_say_time_en(chan, t, ints, lang); + } else if (!strncasecmp(lang, "dr", 2)) { /* Dari syntax */ + return ast_say_time_en(chan, t, ints, lang); } /* Default to English */