MDL-65809 environment: mbstring extension required
[moodle.git] / filter / multilang / filter.php
blob2e9bac35575eef0fb4befea4b5eb7ce98e9c2c92
1 <?php
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.
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/>.
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
25 defined('MOODLE_INTERNAL') || die();
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!
35 // This version is based on original multilang filter by Gaetan Frenoy,
36 // rewritten by Eloy and skodak.
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>
42 /**
43 * Implementation of the Moodle filter API for the Multi-lang filter.
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
49 class filter_multilang extends moodle_text_filter {
50 function filter($text, array $options = array()) {
51 global $CFG;
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?
58 if (empty($text) or is_numeric($text)) {
59 return $text;
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';
70 $result = preg_replace_callback($search, [$this, 'process_match'], $text);
72 if (is_null($result)) {
73 return $text; //error during regex processing (too many nested spans?)
74 } else {
75 return $result;
79 /**
80 * This is the callback used by the preg_replace_callback call above.
82 * @param array $langblock one of the matches from the regex match.
83 * @return string the replacement string (one of the possible translations).
85 protected function process_match(array $langblock): string {
86 $searchtosplit = '/<(?:lang|span)[^>]+lang="([a-zA-Z0-9_-]+)"[^>]*>(.*?)<\/(?:lang|span)>/is';
88 if (!preg_match_all($searchtosplit, $langblock[0], $rawlanglist)) {
89 // Skip malformed blocks.
90 return $langblock[0];
93 $langlist = array();
94 foreach ($rawlanglist[1] as $index => $lang) {
95 $lang = str_replace('-', '_', strtolower($lang)); // Normalize languages.
96 $langlist[$lang] = $rawlanglist[2][$index];
99 // Follow the stream of parent languages.
100 $lang = current_language();
101 do {
102 if (isset($langlist[$lang])) {
103 return $langlist[$lang];
105 } while ($lang = $this->get_parent_lang($lang));
107 // If we don't find a match, default to the first provided translation.
108 return array_shift($langlist);
112 * Puts some caching around get_parent_language().
114 * Also handle parent == 'en' in a way that works better for us.
116 * @param string $lang a Moodle language code, e.g. 'fr'.
117 * @return string the parent language.
119 protected function get_parent_lang(string $lang): string {
120 static $parentcache;
121 if (!isset($parentcache)) {
122 $parentcache = ['en' => ''];
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';
132 return $parentcache[$lang];