Plugin Directory

Changeset 3496018


Ignore:
Timestamp:
03/31/2026 09:44:07 PM (5 days ago)
Author:
mvirik
Message:

Release 3.1.6

Location:
text-to-speech-tts/trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • text-to-speech-tts/trunk/includes/class-mementor-tts-processor.php

    r3494985 r3496018  
    149149        $text = implode(' -- ', $text_parts);
    150150       
     151        // Normalize numbers, currency, and percentages into spoken words
     152        $text = $this->normalize_numbers($text);
     153
    151154        // Apply word replacements before custom pauses
    152155        $text = $this->apply_word_replacements($text);
     
    291294
    292295    /**
     296     * Convert a number to its English spoken form
     297     *
     298     * @param int|float $number The number to convert
     299     * @return string The number in words
     300     */
     301    private function number_to_words($number) {
     302        if (class_exists('NumberFormatter')) {
     303            $fmt = new \NumberFormatter('en', \NumberFormatter::SPELLOUT);
     304            return $fmt->format($number);
     305        }
     306
     307        // Minimal fallback for small numbers if intl extension is missing
     308        $ones = array('', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine',
     309                       'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen',
     310                       'seventeen', 'eighteen', 'nineteen');
     311        $tens = array('', '', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety');
     312
     313        $n = (int) $number;
     314        if ($n < 0) return 'minus ' . $this->number_to_words(-$n);
     315        if ($n === 0) return 'zero';
     316        if ($n < 20) return $ones[$n];
     317        if ($n < 100) return $tens[(int)($n / 10)] . ($n % 10 ? ' ' . $ones[$n % 10] : '');
     318        if ($n < 1000) return $ones[(int)($n / 100)] . ' hundred' . ($n % 100 ? ' ' . $this->number_to_words($n % 100) : '');
     319        if ($n < 1000000) return $this->number_to_words((int)($n / 1000)) . ' thousand' . ($n % 1000 ? ' ' . $this->number_to_words($n % 1000) : '');
     320        if ($n < 1000000000) return $this->number_to_words((int)($n / 1000000)) . ' million' . ($n % 1000000 ? ' ' . $this->number_to_words($n % 1000000) : '');
     321        return $this->number_to_words((int)($n / 1000000000)) . ' billion' . ($n % 1000000000 ? ' ' . $this->number_to_words($n % 1000000000) : '');
     322    }
     323
     324    /**
     325     * Normalize numbers, currency, and percentages into spoken words
     326     * so TTS engines pronounce them naturally.
     327     *
     328     * Handles: $1,234  $1234  1,234  1234  12.5%  1st/2nd/3rd/4th  1.5  etc.
     329     *
     330     * @param string $text The text to process
     331     * @return string Text with numbers converted to spoken words
     332     */
     333    private function normalize_numbers($text) {
     334        // 1. Currency: $1,234.56 or $1234 (with optional cents)
     335        $text = preg_replace_callback(
     336            '/\$\s?([\d,]+)(?:\.([\d]{1,2}))?/',
     337            function ($m) {
     338                $whole = (int) str_replace(',', '', $m[1]);
     339                $words = $this->number_to_words($whole);
     340                if ($whole === 1) {
     341                    $words .= ' dollar';
     342                } else {
     343                    $words .= ' dollars';
     344                }
     345                if (!empty($m[2])) {
     346                    $cents = (int) $m[2];
     347                    if (strlen($m[2]) === 1) $cents *= 10; // $1.5 → 50 cents
     348                    if ($cents > 0) {
     349                        $words .= ' and ' . $this->number_to_words($cents);
     350                        $words .= ($cents === 1) ? ' cent' : ' cents';
     351                    }
     352                }
     353                return $words;
     354            },
     355            $text
     356        );
     357
     358        // 2. Percentages: 2% or 12.5%
     359        $text = preg_replace_callback(
     360            '/(\d+(?:\.\d+)?)\s?%/',
     361            function ($m) {
     362                $num = (float) $m[1];
     363                if (floor($num) == $num) {
     364                    return $this->number_to_words((int) $num) . ' percent';
     365                }
     366                $parts = explode('.', $m[1]);
     367                return $this->number_to_words((int) $parts[0]) . ' point ' .
     368                       implode(' ', array_map(function($d) { return $this->number_to_words((int) $d); }, str_split($parts[1]))) .
     369                       ' percent';
     370            },
     371            $text
     372        );
     373
     374        // 3. Ordinals: 1st, 2nd, 3rd, 4th, 21st, etc.
     375        $text = preg_replace_callback(
     376            '/\b(\d+)(st|nd|rd|th)\b/i',
     377            function ($m) {
     378                $n = (int) $m[1];
     379                if (class_exists('NumberFormatter')) {
     380                    $fmt = new \NumberFormatter('en', \NumberFormatter::SPELLOUT);
     381                    $fmt->setTextAttribute(\NumberFormatter::DEFAULT_RULESET, '%spellout-ordinal');
     382                    return $fmt->format($n);
     383                }
     384                return $this->number_to_words($n) . $m[2];
     385            },
     386            $text
     387        );
     388
     389        // 4. Plain large numbers with commas: 1,234 or 1,234,567
     390        //    (only when not already handled by currency above)
     391        $text = preg_replace_callback(
     392            '/\b(\d{1,3}(?:,\d{3})+)\b/',
     393            function ($m) {
     394                $num = (int) str_replace(',', '', $m[1]);
     395                return $this->number_to_words($num);
     396            },
     397            $text
     398        );
     399
     400        // 5. Standalone large numbers (4+ digits, no commas) that TTS may mispronounce
     401        //    e.g. 22200 → "twenty-two thousand two hundred"
     402        //    Skip numbers that look like years (1900-2099) in natural context
     403        $text = preg_replace_callback(
     404            '/\b(\d{4,})\b/',
     405            function ($m) {
     406                $num = (int) $m[1];
     407                // Preserve 4-digit numbers in year range — TTS handles these fine
     408                if ($num >= 1900 && $num <= 2099 && strlen($m[1]) === 4) {
     409                    return $m[0];
     410                }
     411                return $this->number_to_words($num);
     412            },
     413            $text
     414        );
     415
     416        return $text;
     417    }
     418
     419    /**
    293420     * Apply word replacements to text
    294      * 
     421     *
    295422     * @param string $text The text to process
    296423     * @return string Text with word replacements applied
     
    13021429        }
    13031430       
     1431        // Normalize numbers, currency, and percentages into spoken words
     1432        $processed_text = $this->normalize_numbers($processed_text);
     1433
    13041434        // Apply word replacements before custom pauses
    13051435        $processed_text = $this->apply_word_replacements($processed_text);
    1306        
     1436
    13071437        // Apply custom pauses to the processed text
    13081438        $processed_text = $this->apply_custom_pauses($processed_text);
  • text-to-speech-tts/trunk/readme.txt

    r3496011 r3496018  
    66Tested up to: 6.9
    77Requires PHP: 7.2
    8 Stable tag: 3.1.5
     8Stable tag: 3.1.6
    99License: GPLv3 or later
    1010License URI: [https://www.gnu.org/licenses/gpl-3.0.txt](https://www.gnu.org/licenses/gpl-3.0.txt)
     
    236236
    237237== Changelog ==
     238
     239= 3.1.6 - 2026-03-31 =
     240
     241* New: Numbers, currency, and percentages are now converted to spoken words before synthesis (e.g. $22,200 → "twenty-two thousand two hundred dollars", 2% → "two percent")
     242* Fixed: Shortcode audio generator was missing number normalization, causing TTS to mispronounce amounts
     243* Fixed: ElevenLabs Playground was sending raw text without any preprocessing
    238244
    239245= 3.1.5 - 2026-03-31 =
  • text-to-speech-tts/trunk/text-to-speech-tts.php

    r3496011 r3496018  
    99 * Plugin URI:        https://mementor.no/en/wordpress-plugins/text-to-speech/
    1010 * Description:       The easiest Text-to-Speech plugin for WordPress. Add natural voices, boost accessibility, and engage visitors with an instant audio player.
    11  * Version:           3.1.5
     11 * Version:           3.1.6
    1212 * Author:            Mementor AS
    1313 * Author URI:        https://mementor.no/en/
     
    2626
    2727// Define plugin constants
    28 define('MEMENTOR_TTS_VERSION', '3.1.5');
     28define('MEMENTOR_TTS_VERSION', '3.1.6');
    2929define('MEMENTOR_TTS_PLUGIN_DIR', plugin_dir_path(__FILE__));
    3030define('MEMENTOR_TTS_PLUGIN_URL', plugin_dir_url(__FILE__));
Note: See TracChangeset for help on using the changeset viewer.