How to encapsulate and share Drupal-translatable strings and markup

Keywords

Adapted from Coder: Issue: This requirement is at odds with OO encapsulation: WARNING | Only string literals should be passed to t() where possible:


I have explored various ways of handling valid Drupal-translatable strings in a way that can be shared across an application, so that the Don't Repeat Yourself (DRY) principle is respected.

For the sake of this illustration I have a class DemoCommon that should be usable by external client classes.

It will offer a marked up string that explains that one can hover over links with a mouse to obtain more info, which string should be translatable.

Firstly, some things that don't work and why:

class DemoCommon  {
 
  const MARKUP_TIP_HOVER_LINKS
   = 'For more information on each demo please hover the mouse over each link:';
  // No ! Can't be Drupal-translated using t(self::MARKUP_TIP_HOVER_LINKS).
..
}

This was the initial point of this issue post. The Drupal translation registry strategy
does not see the encapsulated shared string in t(self::MARKUP_TIP_HOVER_LINKS).

This also does not work, applying t() in the initialiser:

class DemoCommon  {
 
  const MARKUP_TIP_HOVER_LINKS
   = t('For more information on each demo please hover the mouse over each link:');
  //  No ! PHP const can't be result of calculation.
..
}

Because PHP const can't be result of calculation this fails, however note that PHP5.6 will apparently offer more initialisation options.

Just for the record, trying to make a static class variable act as a const in PHP does not work:

class DemoCommon  {
 
  final static private $MARKUP_TIP_HOVER_LINKS = ..
  // No ! Unlike Java, PHP does not support final on static class variables.
 
}

Unlike Java, PHP does not support final on static class variables.

One also can't initialise a static class variable with t() already:

class DemoCommon  {
  static private $MARKUP_TIP_HOVER_LINKS =
     t('For more information on each demo please hover the mouse over each link:');
  // No ! PHP5.3 does not support calculation in initializers.
}

I now show 3 strategies that work, with various pros and cons.

One can post-initialize a private static class variable like this (adapted from http://stackoverflow.com/questions/693691/how-to-initialize-static-varia...):

class DemoCommon  {
 
  /**
   * For demonstration of complex static init in PHP5.3.
   * 
   * It will be initialized with a Drupal-translated string.
   * 
   * Note that because it can't be set to final, it must
   * remain private, otherwise the outside world could
   * change it after initialization.
   * 
   * @var string
   */
  static private $T_MARKUP_TIP_HOVER_LINKS;
 
  /**
   * Demonstrates initialising complex static class variables.
   * 
   * Used here to initialize a shareable Drupal-tranlsated string.
   */
  static public final function init() {
    self::$T_MARKUP_TIP_HOVER_LINKS =
        t('For more information on each demo please hover the mouse over each link:');
  }
 
  static public function useMeInternallyOfferToPublic() { 
    $tip = self::$T_MARKUP_TIP_HOVER_LINKS;
    // Do something with $tip.
  }
}
DemoCommon::init();
// So complex static class variables initialized. 

This has the disadvantage that one can only safely use the private shared translated string internally (perhaps value adding before returning via another public method). This is because one can't in PHP make the static class variable final, and if it is made public it is not safe against external manipulation.

Another simpler approach is to use a public method that simply translates the string and returns it:

class DemoCommon  {
  /**
   * A translated shareable message about info when hovering mouse over links.
   * 
   * One way of sharing a Drupal-translatable string (DRY principle).
   * 
   * @return string
   *   A translated shareable message.
   */
  static public final function tMarkupTipHoverLinks() {
    return t('For more information on each demo please hover the mouse over each link:');
    // Could be combined with lazy init on a private static class variable.
  }
}

This can then easily be used externally as DemoCommon::tMarkupTipHoverLinks().

However, the following with lazy init gives better performance if it is used frequently:

class DemoCommon  {
 
  /**
   * For demonstration of complex static init in PHP5.3.
   * 
   * It will be lazily initialized with a Drupal-translated string.
   * 
   * @var string
   */
  static private $T_MARKUP_TIP_HOVER_LINKS_LAZY;
 
  /**
   * A translated shareable message about info when hovering mouse over links.
   * 
   * One way of sharing a Drupal-translatable string (DRY principle).
   * 
   * This version uses lazy initialization.
   * 
   * @return string
   *   A translated shareable message.
   */
  static public final function tMarkupTipHoverLinksLazy() {
    if (!isset(self::$T_MARKUP_TIP_HOVER_LINKS_LAZY)) {
      self::$T_MARKUP_TIP_HOVER_LINKS_LAZY =
          t('For more information on each demo please hover the mouse over each link:');
    }
    return self::$T_MARKUP_TIP_HOVER_LINKS_LAZY;
  }
}

This will be populated on first access as DemoCommon::tMarkupTipHoverLinksLazy().

As far as I can tell, all 3 solutions above are compatible with the Drupal localisation registry strategy (searching for t('string literal') in code) while also respecting the DRY principle (avoiding error prone repetition of the string and/or the translation of the string).

Any feedback welcome.

Visit also