Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.
   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * @package    filter_multilang
  20   * @copyright  Gaetan Frenoy <gaetan@frenoy.net>
  21   * @copyright  2004 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  defined('MOODLE_INTERNAL') || die();
  26  
  27  // Given XML multilinguage text, return relevant text according to
  28  // current language:
  29  //   - look for multilang blocks in the text.
  30  //   - if there exists texts in the currently active language, print them.
  31  //   - else, if there exists texts in the current parent language, print them.
  32  //   - else, print the first language in the text.
  33  // Please note that English texts are not used as default anymore!
  34  //
  35  // This version is based on original multilang filter by Gaetan Frenoy,
  36  // rewritten by Eloy and skodak.
  37  //
  38  // Following new syntax is not compatible with old one:
  39  //   <span lang="XX" class="multilang">one lang</span><span lang="YY" class="multilang">another language</span>
  40  
  41  
  42  /**
  43   * Implementation of the Moodle filter API for the Multi-lang filter.
  44   *
  45   * @copyright  Gaetan Frenoy <gaetan@frenoy.net>
  46   * @copyright  2004 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
  47   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  48   */
  49  class filter_multilang extends moodle_text_filter {
  50      function filter($text, array $options = array()) {
  51          global $CFG;
  52  
  53          // [pj] I don't know about you but I find this new implementation funny :P
  54          // [skodak] I was laughing while rewriting it ;-)
  55          // [nicolasconnault] Should support inverted attributes: <span class="multilang" lang="en"> (Doesn't work curently)
  56          // [skodak] it supports it now, though it is slower - any better idea?
  57  
  58          if (empty($text) or is_numeric($text)) {
  59              return $text;
  60          }
  61  
  62          if (empty($CFG->filter_multilang_force_old) and !empty($CFG->filter_multilang_converted)) {
  63              // new syntax
  64              $search = '/(<span(\s+lang="[a-zA-Z0-9_-]+"|\s+class="multilang"){2}\s*>.*?<\/span>)(\s*<span(\s+lang="[a-zA-Z0-9_-]+"|\s+class="multilang"){2}\s*>.*?<\/span>)+/is';
  65          } else {
  66              // old syntax
  67              $search = '/(<(?:lang|span) lang="[a-zA-Z0-9_-]*".*?>.*?<\/(?:lang|span)>)(\s*<(?:lang|span) lang="[a-zA-Z0-9_-]*".*?>.*?<\/(?:lang|span)>)+/is';
  68          }
  69  
  70          $result = preg_replace_callback($search, [$this, 'process_match'], $text);
  71  
  72          if (is_null($result)) {
  73              return $text; //error during regex processing (too many nested spans?)
  74          } else {
  75              return $result;
  76          }
  77      }
  78  
  79      /**
  80       * This is the callback used by the preg_replace_callback call above.
  81       *
  82       * @param array $langblock one of the matches from the regex match.
  83       * @return string the replacement string (one of the possible translations).
  84       */
  85      protected function process_match(array $langblock): string {
  86          $searchtosplit = '/<(?:lang|span)[^>]+lang="([a-zA-Z0-9_-]+)"[^>]*>(.*?)<\/(?:lang|span)>/is';
  87  
  88          if (!preg_match_all($searchtosplit, $langblock[0], $rawlanglist)) {
  89              // Skip malformed blocks.
  90              return $langblock[0];
  91          }
  92  
  93          $langlist = array();
  94          foreach ($rawlanglist[1] as $index => $lang) {
  95              $lang = str_replace('-', '_', strtolower($lang)); // Normalize languages.
  96              $langlist[$lang] = $rawlanglist[2][$index];
  97          }
  98  
  99          // Follow the stream of parent languages.
 100          $lang = current_language();
 101          do {
 102              if (isset($langlist[$lang])) {
 103                  return $langlist[$lang];
 104              }
 105          } while ($lang = $this->get_parent_lang($lang));
 106  
 107          // If we don't find a match, default to the first provided translation.
 108          return array_shift($langlist);
 109      }
 110  
 111      /**
 112       * Puts some caching around get_parent_language().
 113       *
 114       * Also handle parent == 'en' in a way that works better for us.
 115       *
 116       * @param string $lang a Moodle language code, e.g. 'fr'.
 117       * @return string the parent language.
 118       */
 119      protected function get_parent_lang(string $lang): string {
 120          static $parentcache;
 121          if (!isset($parentcache)) {
 122              $parentcache = ['en' => ''];
 123          }
 124          if (!isset($parentcache[$lang])) {
 125              $parentcache[$lang] = get_parent_language($lang);
 126              // The standard get_parent_language method returns '' for parent == 'en'.
 127              // That is less helpful for us, so change it back.
 128              if ($parentcache[$lang] === '') {
 129                  $parentcache[$lang] = 'en';
 130              }
 131          }
 132          return $parentcache[$lang];
 133      }
 134  }