Parse the phone number withsscanf() or preg_match(), then translate with PHP's native numeric text formatter, and conditionally handle leading zeros. Demo $usPhone = '123-456-7809'; $digits = sscanf($usPhone, '%1s%1s%1s-%1s%1s%1s-%2s%2s'); $f = new NumberFormatter('en', NumberFormatter::SPELLOUT); echo implode( ' ', array_map( fn($d) => ($d !== (string)(int)$d ? 'oh ' : '') . (!(int)$d ? 'oh' : $f->format((int)$d)), $digits ) );Output one two three four five six seventy-eight oh nine103-050-0000 becomes: 103-050-4050 becomes: Here is another approach using preg_replace_callback() to which substrings get translated and how. Demo $regex = <<<'REGEX' ~ (?: ([1-9]\d)(?=(?:\d{2})?$) # 1: double-digit in last segment |(0) # 2: zero |([1-9]) # 3: single digit ) -? # consume optional delimiter ~x REGEX; $f = new NumberFormatter('en', NumberFormatter::SPELLOUT); echo ltrim( preg_replace_callback( $regex, fn($m) => sprintf( ' %s', array_key_last($m) === 2 ? 'oh' : $f->format(intval($m[0])) ), $usPhone ) );As a deviation of the original requirements to help developers with phone numbers to Australian speech, here is a version which does not respect two-digit values in the last segment, but respects, hundreds, thousands, etc. as well as multiple consecutive numbers. Demo $regex = <<<'REGEX' ~ (?: \b([1-9]0{2,})\b # 1: hundreds, thousands, etc |(\d)\2+ # 2: multiples |(0) # 3: zero |([1-9]) # 4: single digit ) -? # consume optional delimiter ~x REGEX; $f = new NumberFormatter('en', NumberFormatter::SPELLOUT); $mapping = [ 2 => 'double', 3 => 'triple', 4 => 'quadruple', 5 => 'quintuple', 6 => 'sextuple', 7 => 'septuple', 8 => 'octuple', 9 => 'nonuple', 10 => 'decuple', ]; echo ltrim( preg_replace_callback( $regex, fn($m) => ' ' . match (array_key_last($m)) { 1, 4 => $f->format((int)$m[0]), 2 => $mapping[strlen(rtrim($m[0], '-'))] . " " . ($m[2] ? $f->format((int)$m[2]) : 'oh'), default => 'oh', }, $usPhone ) );$usPhone = '800-000-7755' becomes eight hundred triple oh double seven double five (责任编辑:) |