How to end the excerpt with a sentence rather than a word?

Here is the excerpt of the post on my home page. I want to end the excerpt with a sentence. In this case it is ‘ opportunities. ‘. I know how to modify the length but it won’t do what i want.

enter image description here

Read More

Here is excerpt from other website. Here you see excerpt ending with a proper sentence unlike the above which ends with the first word of next sentence ‘Proactively’.

enter image description here

After Implementing the solution by G.M. :-

enter image description here

Related posts

4 comments

  1. This requires PHP 5.3+ (WP requires PHP 5.2.4+)

    add_filter('get_the_excerpt', 'end_with_sentence');
    
    function end_with_sentence($excerpt) {
      $allowed_end = array('.', '!', '?', '...');
      $exc = explode( ' ', $excerpt );
      $found = false;
      $last = '';
      while ( ! $found && ! empty($exc) ) { 
        $last = array_pop($exc);
        $end = strrev( $last );
        $found = in_array( $end{0}, $allowed_end );
      }
      return (! empty($exc)) ? $excerpt : rtrim(implode(' ', $exc) . ' ' .$last);
    }
    

    Edit

    After @kaiser comment I try to run this on save/update to prevent page slow down on showing a lot of posts. This should be tested

    add_filter('wp_insert_post_data', 'end_with_sentence_on_save', 20, 2);
    
    function end_with_sentence_on_save($data, $postarr) {
      if ( ! empty( $data['post_content'] ) && $data['post_status'] != 'inherit' && $data['post_status'] != 'trash' ) {
        $text = strip_shortcodes( $data['post_content'] );
        $text = apply_filters('the_content', $text );
        $text = str_replace(']]>', ']]>', $text );
        $excerpt_length = apply_filters('excerpt_length', 55);
        $data['post_excerpt'] = wp_trim_words($text, $excerpt_length, '');
      } else {
        return $data;
      }
      $allowed_end = array('.', '!', '?', '...');
      $exc = explode(' ', $data['post_excerpt']);
      $found = false;
      $last = '';
      while ( ! $found && ! empty($exc) ) { 
        $last = array_pop($exc);
        $end = strrev( $last );
        $found = in_array( $end{0}, $allowed_end );
      }
      if (! empty($exc)) $data['post_excerpt'] = rtrim(implode(' ', $exc) . ' ' .$last);
      return $data; 
    }
    
  2. Here is my version, keeping all html tags and also trimming the content after the last word in of a sentence

    if ( ! function_exists( 'pietergoosen_custom_wp_trim_excerpt' ) ) : 
    
        function pietergoosen_custom_wp_trim_excerpt($pietergoosen_excerpt) {
        global $post;
        $raw_excerpt = $pietergoosen_excerpt;
            if ( '' == $pietergoosen_excerpt ) {
    
                $pietergoosen_excerpt = get_the_content('');
                $pietergoosen_excerpt = strip_shortcodes( $pietergoosen_excerpt );
                $pietergoosen_excerpt = apply_filters('the_content', $pietergoosen_excerpt);
                $pietergoosen_excerpt = str_replace(']]>', ']]>', $pietergoosen_excerpt);
    
                //Set the excerpt word count and only break after sentence is complete.
                    $excerpt_word_count = 75;
                    $excerpt_length = apply_filters('excerpt_length', $excerpt_word_count); 
                    $tokens = array();
                    $excerptOutput = '';
                    $count = 0;
    
                    // Divide the string into tokens; HTML tags, or words, followed by any whitespace
                    preg_match_all('/(<[^>]+>|[^<>s]+)s*/u', $pietergoosen_excerpt, $tokens);
    
                    foreach ($tokens[0] as $token) { 
    
                        if ($count >= $excerpt_word_count && preg_match('/[?.!]s*$/uS', $token)) { 
                        // Limit reached, continue until  ? . or ! occur at the end
                            $excerptOutput .= trim($token);
                            break;
                        }
    
                        // Add words to complete sentence
                        $count++;
    
                        // Append what's left of the token
                        $excerptOutput .= $token;
                    }
    
                $pietergoosen_excerpt = trim(force_balance_tags($excerptOutput));
    
                    $excerpt_end = ' <a href="'. esc_url( get_permalink() ) . '">' . '&nbsp;&raquo;&nbsp;' . sprintf(__( 'Read more about: %s &nbsp;&raquo;', 'pietergoosen' ), get_the_title()) . '</a>'; 
                    $excerpt_more = apply_filters('excerpt_more', ' ' . $excerpt_end); 
    
                    //$pos = strrpos($pietergoosen_excerpt, '</');
                    //if ($pos !== false)
                    // Inside last HTML tag
                    //$pietergoosen_excerpt = substr_replace($pietergoosen_excerpt, $excerpt_end, $pos, 0);
                    //else
                    // After the content
                    $pietergoosen_excerpt .= $excerpt_end;
    
                return $pietergoosen_excerpt;   
    
            }
            return apply_filters('pietergoosen_custom_wp_trim_excerpt', $pietergoosen_excerpt, $raw_excerpt);
        }
    
    endif; 
    
    remove_filter('get_the_excerpt', 'wp_trim_excerpt');
    add_filter('get_the_excerpt', 'pietergoosen_custom_wp_trim_excerpt'); 
    
  3. Just in case someone is using the answer, word of advice: This will ruin the Advanced Custom Fields. ACF also invokes wp_insert_post_data when creating or updating a field, but the $data variable is not the same, and will result in the ACF field name to become some unreadable gibberish, which results in not being able to use the field.

  4. Here is a different solution, which uses WordPress in-built wp_trim_words() function to determine roughly how long the text should be, and then truncates the text at the end of that sentence (a little longer than the number of words specified).

    Usage: echo im_trim_sentences($very_long_text, 90);

    A custom “strpos_array()” function has also been included in this answer, because PHP’s strpos() function only accepts a string as the $needle, but we want to search for different sentence endings (full stop, or exclamation mark, etc).

    function strpos_array($haystack, $needles, $offset = 0) {
        if (is_array($needles)) {
            $positions = array();
            foreach ($needles as $str) {
                $pos = strpos($haystack, $str, $offset);
                if ($pos !== false) {
                    $positions[] = $pos;
                }
            }
            return (count($positions) ? min($positions): false);
        } else {
            return strpos($haystack, $needles, $offset);
        }
    }
    
    function im_trim_sentences($text, $number_of_words = 55, $more = null) {
        $allowed_end = array('.', '!', '?', '...');
        $text_no_html = strip_tags($text);
        $trimmed_text = wp_trim_words($text, $number_of_words, $more);
        $trimmed_text_length = strlen($trimmed_text);
        $sentence_end_position = strpos_array($text_no_html, $allowed_end, $trimmed_text_length);
        $text_with_html = (($sentence_end_position !== false) ? substr($text_no_html, 0, ($sentence_end_position + 1)) : $trimmed_text);
        return wpautop($text_with_html);
    }
    

Comments are closed.