Merge pull request #3580 from monishdeb/CRM-14701
[civicrm-core.git] / CRM / Core / I18n.php
index 56f3b39ede2624828953e010d382373a9dd481e8..af6c536d5518eaab19fe82c2ab17eb520a7a0179 100644 (file)
@@ -1,7 +1,7 @@
 <?php
 /*
  +--------------------------------------------------------------------+
- | CiviCRM version 4.3                                                |
+ | CiviCRM version 4.4                                                |
  +--------------------------------------------------------------------+
  | Copyright CiviCRM LLC (c) 2004-2013                                |
  +--------------------------------------------------------------------+
@@ -35,7 +35,8 @@
 class CRM_Core_I18n {
 
   /**
-   * A PHP-gettext instance for string translation; should stay null if the strings are not to be translated (en_US).
+   * A PHP-gettext instance for string translation;
+   * should stay null if the strings are not to be translated (en_US).
    */
   private $_phpgettext = NULL;
 
@@ -44,6 +45,13 @@ class CRM_Core_I18n {
    */
   private $_nativegettext = FALSE;
 
+  /**
+   * Gettext cache for extension domains/streamers, depending on if native or phpgettext.
+   * - native gettext: we cache the value for textdomain()
+   * - phpgettext: we cache the file streamer.
+   */
+  private $_extensioncache = array();
+
   /**
    * A locale-based constructor that shouldn't be called from outside of this class (use singleton() instead).
    *
@@ -73,15 +81,27 @@ class CRM_Core_I18n {
         textdomain('civicrm');
 
         $this->_phpgettext = new CRM_Core_I18n_NativeGettext();
+        $this->_extensioncache['civicrm'] = 'civicrm';
         return;
       }
 
       // Otherwise, use PHP-gettext
+      // we support both the old file hierarchy format and the new:
+      // pre-4.5:  civicrm/l10n/xx_XX/civicrm.mo
+      // post-4.5: civicrm/l10n/xx_XX/LC_MESSAGES/civicrm.mo
       require_once 'PHPgettext/streams.php';
       require_once 'PHPgettext/gettext.php';
 
-      $streamer = new FileReader($config->gettextResourceDir . $locale . DIRECTORY_SEPARATOR . 'civicrm.mo');
+      $mo_file = $config->gettextResourceDir . $locale . DIRECTORY_SEPARATOR . 'LC_MESSAGES' . DIRECTORY_SEPARATOR . 'civicrm.mo';
+
+      if (! file_exists($mo_file)) {
+        // fallback to pre-4.5 mode
+        $mo_file = $config->gettextResourceDir . $locale . DIRECTORY_SEPARATOR . 'civicrm.mo';
+      }
+
+      $streamer = new FileReader($mo_file);
       $this->_phpgettext = new gettext_reader($streamer);
+      $this->_extensioncache['civicrm'] = $this->_phpgettext;
     }
   }
 
@@ -231,7 +251,7 @@ class CRM_Core_I18n {
 
     // gettext domain for extensions
     $domain_changed = FALSE;
-    if (isset($params['domain'])) {
+    if (! empty($params['domain']) && $this->_phpgettext) {
       if ($this->setGettextDomain($params['domain'])) {
         $domain_changed = TRUE;
       }
@@ -239,7 +259,8 @@ class CRM_Core_I18n {
 
     // do all wildcard translations first
     $config = CRM_Core_Config::singleton();
-    $stringTable = CRM_Utils_Array::value($config->lcMessages,
+    $stringTable = CRM_Utils_Array::value(
+      $config->lcMessages,
       $config->localeCustomStrings
     );
 
@@ -254,15 +275,13 @@ class CRM_Core_I18n {
       }
     }
 
-    if (!$exactMatch &&
+    if (
+      !$exactMatch &&
       isset($stringTable['enabled']['wildcardMatch'])
     ) {
       $search  = array_keys($stringTable['enabled']['wildcardMatch']);
       $replace = array_values($stringTable['enabled']['wildcardMatch']);
-      $text    = str_replace($search,
-        $replace,
-        $text
-      );
+      $text    = str_replace($search, $replace, $text);
     }
 
     // dont translate if we've done exactMatch already
@@ -378,13 +397,16 @@ class CRM_Core_I18n {
    *
    * @param  $key Key of the extension (can be 'civicrm', or 'org.example.foo').
    *
-   * @return void
+   * @return Boolean True if the domain was changed for an extension.
    */
   function setGettextDomain($key) {
-    static $cache = array();
+    /* No domain changes for en_US */
+    if (! $this->_phpgettext) {
+      return FALSE;
+    }
 
-    // It's only necessary to find once
-    if (! isset($cache[$key])) {
+    // It's only necessary to find/bind once
+    if (! isset($this->_extensioncache[$key])) {
       $config = CRM_Core_Config::singleton();
 
       try {
@@ -393,19 +415,38 @@ class CRM_Core_I18n {
         $info = $mapper->keyToInfo($key);
         $domain = $info->file;
 
-        bindtextdomain($domain, $path . DIRECTORY_SEPARATOR . 'l10n');
-        bind_textdomain_codeset($domain, 'UTF-8');
-        $cache[$key] = $domain;
+        if ($this->_nativegettext) {
+          bindtextdomain($domain, $path . DIRECTORY_SEPARATOR . 'l10n');
+          bind_textdomain_codeset($domain, 'UTF-8');
+          $this->_extensioncache[$key] = $domain;
+        }
+        else {
+          // phpgettext
+          $mo_file = $path . DIRECTORY_SEPARATOR . 'l10n' . DIRECTORY_SEPARATOR . $config->lcMessages . DIRECTORY_SEPARATOR . 'LC_MESSAGES' . DIRECTORY_SEPARATOR . $domain . '.mo';
+          $streamer = new FileReader($mo_file);
+          $this->_extensioncache[$key] = new gettext_reader($streamer);
+        }
       }
       catch (CRM_Extension_Exception $e) {
-        // There's not much we can do at this point
-        $cache[$key] = FALSE;
+        // Intentionally not translating this string to avoid possible infinit loops
+        // Only developers should see this string, if they made a mistake in their ts() usage.
+        CRM_Core_Session::setStatus('Unknown extension key in a translation string: ' . $key, '', 'error');
+        $this->_extensioncache[$key] = FALSE;
       }
     }
 
-    if (isset($cache[$key]) && $cache[$key]) {
-      textdomain($cache[$key]);
+    if (isset($this->_extensioncache[$key]) && $this->_extensioncache[$key]) {
+      if ($this->_nativegettext) {
+        textdomain($this->_extensioncache[$key]);
+      }
+      else {
+        $this->_phpgettext = $this->_extensioncache[$key];
+      }
+
+      return TRUE;
     }
+
+    return FALSE;
   }
 
   /**