Dynamic Content API localisation

Hi followed the latest video with @DoncoMarketing showing the read time - really cool as I was using a custom php function before and putting it in via a shortcode.

I tried to evolve getting more than just the number so it would deal with min/mins and hr/hrs (not that many blogs are that long!).

But anything I’ve tried, it outputs formatted like 2,2 mins for example. It seems to be localising it were it shouldn’t?

 class ReadTimeDynamicContent {

  const GROUP = 'custom_dynamic_content';

  public static function setup() {
    add_filter('cs_dynamic_content_' . self::GROUP, [self::class, 'calculateReadTime'], 0, 4);
    add_action('cs_dynamic_content_setup', [self::class, 'register']);
  }

  public static function register() {
    cornerstone_dynamic_content_register_group([
      'name'  => self::GROUP,
      'label' => 'Custom Dynamic Content',
    ]);
    cornerstone_dynamic_content_register_field([
      'name'   => 'reading_time',
      'group'  => self::GROUP,
      'label'  => 'Reading Time',
      'controls' => [
        [
          'key'   => 'minutes',
          // Use 'text' for safer output in frontend display,
          // but if you want raw number for calculations elsewhere, use 'number'
          'type'  => 'text',
          'label' => 'Minutes',
        ],
        [
          'key'   => 'formatted',
          'type'  => 'text',
          'label' => 'Formatted',
        ],
      ],
    ]);
  }

  public static function calculateReadTime($result, $field, $args = []) {
    switch ($field) {
      case 'reading_time':
        $content = get_post_field('post_content', get_the_ID()); // Get current post content
        $word_count = str_word_count(strip_tags($content));
        // Calculate total minutes, always integer
        $total_minutes = max(1, (int) ceil($word_count / 200));
        $hours   = (int) floor($total_minutes / 60);
        $minutes = $total_minutes % 60;

        // Pluralization logic for hrs/mins
        $formatted = '';
        if ($hours > 0) {
          $formatted .= $hours . ' ' . ($hours === 1 ? 'hr' : 'hrs');
          if ($minutes > 0) $formatted .= ' ';
        }
        if ($minutes > 0 || $hours === 0) {
          $formatted .= $minutes . ' ' . ($minutes === 1 ? 'min' : 'mins');
        }

        $result = [
          // Always output as string to ensure "text" field type works as expected and avoids localization
          'minutes'   => (string) $total_minutes,
          'formatted' => $formatted,
        ];
        break;
      default:
        break;
    }
    return $result;
  }
}

add_action('init', [ReadTimeDynamicContent::class, 'setup']);

Hello @RubberDuckers.

Thank you for the inquiry.

It seems to be working on our end, as shown in the screenshot below. However, we noticed that the controls minutes and formatted are not actually used in the calculateReadTime function, so they can be removed if necessary.

We may need to inspect the page to better understand the issue. Please create a test page and provide the login details in the secure note.

Best regards,

Thanks @Ismael - yeah I don’t think the minutes/formatted were actually used so have removed.
It’s still not quite right for me, will add login details/instructions in a note.

Thank you for the info. The “Read time: DC API” snippet has not been updated with your latest changes, so it only displays or returns the minute/s string or value. You may have forgotten to click “Update” or “Save” after making the modifications. Please check the screenshot below.

Try adding the changes again, save them, and let us know if the “min/s” part still doesn’t display.

Yep working now, thanks! No idea why it was initially passing out 2,2 but working with the min/s now.

Code incase anyone needs:

<?php
class ReadTimeDynamicContent {

  const GROUP = 'custom_dynamic_content';

  public static function setup() {
    add_filter('cs_dynamic_content_' . self::GROUP, [self::class, 'calculateReadTime'], 0, 4);
    add_action('cs_dynamic_content_setup', [self::class, 'register']);
  }

  public static function register() {
    cornerstone_dynamic_content_register_group([
      'name'  => self::GROUP,
      'label' => 'Custom Dynamic Content',
    ]);
    cornerstone_dynamic_content_register_field([
      'name'     => 'reading_time',
      'group'    => self::GROUP,
      'label'    => 'Reading Time',
      'controls' => [], // No controls needed
    ]);
  }

  public static function calculateReadTime($result, $field, $args = []) {
    switch ($field) {
      case 'reading_time':
        $content = get_post_field('post_content', get_the_ID());
        $word_count = str_word_count(strip_tags($content));
        $total_minutes = max(1, (int) ceil($word_count / 180));
        $result = $total_minutes . ' ' . ($total_minutes === 1 ? 'min' : 'mins');
        break;
    }
    return $result;
  }
}

add_action('init', [ReadTimeDynamicContent::class, 'setup']);

Hey @RubberDuckers,

Good to know that it works now. Thank you for sharing the working code as well.

Cheers.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.