Localized number format in Drupal 8

By Ronald van Belzen | July 14, 2016

When building this blog I decided to use the core module statistics to display the view count on blog posts. Because some of my older blog posts have had a lot of views, the counter on those blog post views had grown quite large. For making those large numbers more readable it would be nice that those numbers would be displayed with thousands-seperators, just as can be accomplished with the PHP function number_format() or, even better, with the NumberFormatter class. The statistics module does not do that.

As it turns out Drupal 8 core does not support that either, and a sole module that offers this functionality has no Drupal 8 version. I decided to make a solution specifically for the statistics module. That module implements the hook hook_node_links_alter() in the statistics.module file to add the view count to the list of node links (as text). So I set out to create a copy of this hook that would override this particular hook.

The module to which I will add this hook will be named mystatistics:

# mystatistics.info.yml
name: 'My Statistics'
type: module
description: 'Module to override the display of the Statistics module.'
package: Other
version: '1.0'
core: '8.x'
dependencies:
  - statistics

In the mystatistics.module file I added the following code:

<?php 

/**
 * @file
 * Hooks specific to the mystatistics module.
 */

use Drupal\node\NodeInterface;

/**
 * Alter the links of a node.
 *
 * @param array &$links
 *   A renderable array representing the node links.
 * @param \Drupal\node\NodeInterface $entity
 *   The node being rendered.
 * @param array &$context
 *   Various aspects of the context in which the node links are going to be
 *   displayed, with the following keys:
 *   - 'view_mode': the view mode in which the node is being viewed
 *   - 'langcode': the language in which the node is being viewed
 *
 * @see \Drupal\node\NodeViewBuilder::renderLinks()
 * @see \Drupal\node\NodeViewBuilder::buildLinks()
 * @see entity_crud
 */
function mystatistics_node_links_alter(array &$links, NodeInterface $entity, array &$context) {
  if ($context['view_mode'] != 'rss') {
    $links['#cache']['contexts'][] = 'user.permissions';
    if (\Drupal::currentUser()->hasPermission('view post access counter')) {
      $statistics = statistics_get($entity->id());
      if ($statistics) {
        if(extension_loaded('intl')) {
          $nf = new \NumberFormatter($context['langcode'], \NumberFormatter::DECIMAL);
          $totalcount = $nf->format($statistics['totalcount']);
        } else {
          \Drupal::logger('mystatistics')
            ->notice('Without the PHP extension Intl the module mystatistics cannot format the view count number.');
          $totalcount = $statistics['totalcount'];
        }
        $statistics_links['statistics_counter']['title'] = 
          \Drupal::translation()->formatPlural($statistics['totalcount'], 
          '1 view', 
          '@totalcount views', 
          array('@totalcount' => $totalcount)
        );
        $links['mystatistics'] = array(
          '#theme' => 'links__node__mystatistics',
          '#links' => $statistics_links,
          '#attributes' => array('class' => array('links', 'inline')),
        );
      }
      $links['#cache']['max-age'] = \Drupal::config('statistics.settings')->get('display_max_age');
    }
  }
}

The first thing to point out in the code is the check for the presence of the intl extension. Drupal 8, in contrast to Symfony2, makes no issue about the presence of this extension and in standard installations it is usually not turned on. The NumberFormatter class is part of this extension. To prevent an error the presence is checked and a notice is logged when the extension is not loaded. Without the presence of the intl extension the module serves no purpose. It is best to inform the admin about this in some way.

The $statistic_links key value 'statistics_counter' must be identical to the key value used in the hook statistics_node_links_alter, so that the theme function template_preprocess_links() will override the existing link item instead of adding another one.

I am guessing that the order in which both hooks are triggered is determined by the dependency of the module mystatistics upon the module statistics. I can't seem to find the documentation that tells me this, but in different environments the result seems to be as expected: mystatistics_node_links_alter overrides the statistic_node_links_alter hook.

Add new comment