1<?php 2/** 3 * Translation Plugin: Simple multilanguage plugin 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <andi@splitbrain.org> 7 * @author Guy Brand <gb@isis.u-strasbg.fr> 8 */ 9 10// must be run within Dokuwiki 11if(!defined('DOKU_INC')) die(); 12 13class action_plugin_autotranslation extends \dokuwiki\Extension\ActionPlugin { 14 15 /** 16 * For the helper plugin 17 * @var helper_plugin_autotranslation 18 */ 19 private $helper = null; 20 21 private $locale; 22 23 /** 24 * Constructor. Load helper plugin 25 */ 26 function __construct() { 27 $this->helper = plugin_load('helper', 'autotranslation'); 28 } 29 30 /** 31 * Register the events 32 */ 33 function register(Doku_Event_Handler $controller) { 34 $scriptName = basename($_SERVER['PHP_SELF']); 35 36 // should the lang be applied to UI? 37 if($this->getConf('translateui')) { 38 switch($scriptName) { 39 case 'js.php': 40 $controller->register_hook('INIT_LANG_LOAD', 'BEFORE', $this, 'translation_js'); 41 $controller->register_hook('JS_CACHE_USE', 'BEFORE', $this, 'translation_jscache'); 42 break; 43 44 case 'ajax.php': 45 $controller->register_hook('INIT_LANG_LOAD', 'BEFORE', $this, 'translate_media_manager'); 46 break; 47 48 case 'mediamanager.php': 49 $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'setJsCacheKey'); 50 break; 51 52 default: 53 $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'setJsCacheKey'); 54 } 55 } 56 57 if($scriptName !== 'js.php' && $scriptName !== 'ajax.php') { 58 $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'translation_hook'); 59 $controller->register_hook('MEDIAMANAGER_STARTED', 'BEFORE', $this, 'translation_hook'); 60 } 61 62 $controller->register_hook('SEARCH_QUERY_PAGELOOKUP', 'AFTER', $this, 'translation_search'); 63 $controller->register_hook('COMMON_PAGETPL_LOAD', 'AFTER', $this, 'page_template_replacement'); 64 } 65 66 /** 67 * Hook Callback. Make current language available as page template placeholder and handle 68 * original language copying 69 * 70 * @param $event 71 * @param $args 72 */ 73 function page_template_replacement(Doku_Event $event, $args) { 74 global $ID; 75 76 // load orginal content as template? 77 if($this->getConf('copytrans') && $this->helper->istranslatable($ID, false)) { 78 // look for existing translations 79 $translations = $this->helper->getAvailableTranslations($ID); 80 if($translations) { 81 // find original language (might've been provided via parameter or use first translation) 82 $orig = (string) $_REQUEST['fromlang']; 83 if(!$orig) $orig = array_shift(array_keys($translations)); 84 85 // load file 86 $origfile = $translations[$orig]; 87 $event->data['tpl'] = io_readFile(wikiFN($origfile)); 88 89 // prefix with warning 90 $warn = io_readFile($this->localFN('totranslate')); 91 if($warn) $warn .= "\n\n"; 92 $event->data['tpl'] = $warn . $event->data['tpl']; 93 94 // show user a choice of translations if any 95 if(count($translations) > 1) { 96 $links = array(); 97 foreach($translations as $t => $l) { 98 $links[] = '<a href="' . wl($ID, array('do' => 'edit', 'fromlang' => $t)) . '">' . $this->helper->getLocalName($t) . '</a>'; 99 } 100 101 msg( 102 sprintf( 103 $this->getLang('transloaded'), 104 $this->helper->getLocalName($orig), 105 join(', ', $links) 106 ) 107 ); 108 } 109 110 } 111 } 112 113 // apply placeholders 114 $event->data['tpl'] = str_replace('@LANG@', $this->helper->realLC(''), $event->data['tpl']); 115 $event->data['tpl'] = str_replace('@TRANS@', $this->helper->getLangPart($ID), $event->data['tpl']); 116 } 117 118 /** 119 * Hook Callback. Load correct translation when loading JavaScript 120 * 121 * @param $event 122 * @param $args 123 */ 124 function translation_js(Doku_Event $event, $args) { 125 global $conf; 126 if(!isset($_GET['lang'])) return; 127 if(!in_array($_GET['lang'], $this->helper->translations)) return; 128 $lang = $_GET['lang']; 129 $event->data = $lang; 130 $conf['lang'] = $lang; 131 } 132 133 /** 134 * Hook Callback. Pass language code to JavaScript dispatcher 135 * 136 * @param $event 137 * @param $args 138 * @return bool 139 */ 140 function setJsCacheKey(Doku_Event $event, $args) { 141 if(!isset($this->locale)) return false; 142 $count = count($event->data['script']); 143 for($i = 0; $i < $count; $i++) { 144 if(!empty($event->data['script'][$i]['src']) && strpos($event->data['script'][$i]['src'], '/lib/exe/js.php') !== false) { 145 $event->data['script'][$i]['src'] .= '&lang=' . hsc($this->locale); 146 } 147 } 148 149 return false; 150 } 151 152 /** 153 * Hook Callback. Make sure the JavaScript is translation dependent 154 * 155 * @param $event 156 * @param $args 157 */ 158 function translation_jscache(Doku_Event $event, $args) { 159 if(!isset($_GET['lang'])) return; 160 if(!in_array($_GET['lang'], $this->helper->translations)) return; 161 162 $lang = $_GET['lang']; 163 // reuse the constructor to reinitialize the cache key 164 if(method_exists($event->data, '__construct')) { 165 // New PHP 5 style constructor 166 $event->data->__construct( 167 $event->data->key . $lang, 168 $event->data->ext 169 ); 170 } else { 171 // Old PHP 4 style constructor - deprecated 172 $event->data->cache( 173 $event->data->key . $lang, 174 $event->data->ext 175 ); 176 } 177 } 178 179 /** 180 * Hook Callback. Translate the AJAX loaded media manager 181 * 182 * @param $event 183 * @param $args 184 */ 185 function translate_media_manager(Doku_Event $event, $args) { 186 global $conf; 187 if(isset($_REQUEST['ID'])) { 188 $id = getID(); 189 $lc = $this->helper->getLangPart($id); 190 } elseif(isset($_SESSION[DOKU_COOKIE]['translationlc'])) { 191 $lc = $_SESSION[DOKU_COOKIE]['translationlc']; 192 } else { 193 return; 194 } 195 if(!$lc) return; 196 197 $conf['lang'] = $lc; 198 $event->data = $lc; 199 } 200 201 /** 202 * Hook Callback. Change the UI language in foreign language namespaces 203 */ 204 function translation_hook(Doku_Event $event, $args) { 205 global $ID; 206 global $lang; 207 global $conf; 208 global $ACT; 209 // redirect away from start page? 210 if($this->conf['redirectstart'] && $ID == $conf['start'] && $ACT == 'show') { 211 $lc = $this->helper->getBrowserLang(); 212 if(!$lc) $lc = $conf['lang']; 213 $this->_redirect($lc.':'.$conf['start']); 214 exit; 215 } 216 217 // Check if we can redirect 218 if($this->getConf('redirectlocalized')){ 219 $this->translation_redirect_localized(); 220 } 221 222 // check if we are in a foreign language namespace 223 $lc = $this->helper->getLangPart($ID); 224 225 // store language in session (for page related views only) 226 if(in_array($ACT, array('show', 'recent', 'diff', 'edit', 'preview', 'source', 'subscribe'))) { 227 $_SESSION[DOKU_COOKIE]['translationlc'] = $lc; 228 } 229 if(!$lc) $lc = $_SESSION[DOKU_COOKIE]['translationlc']; 230 if(!$lc) return; 231 $this->locale = $lc; 232 233 if(!$this->getConf('translateui')) { 234 return true; 235 } 236 237 if(file_exists(DOKU_INC . 'inc/lang/' . $lc . '/lang.php')) { 238 require(DOKU_INC . 'inc/lang/' . $lc . '/lang.php'); 239 } 240 $conf['lang_before_translation'] = $conf['lang']; //store for later access in syntax plugin 241 $conf['lang'] = $lc; 242 243 return true; 244 } 245 246 /** 247 * Hook Callback. Resort page match results so that results are ordered by translation, having the 248 * default language first 249 */ 250 function translation_search(Doku_Event $event, $args) { 251 252 if($event->data['has_titles']) { 253 // sort into translation slots 254 $res = array(); 255 foreach($event->result as $r => $t) { 256 $tr = $this->helper->getLangPart($r); 257 if(!is_array($res["x$tr"])) $res["x$tr"] = array(); 258 $res["x$tr"][] = array($r, $t); 259 } 260 // sort by translations 261 ksort($res); 262 // combine 263 $event->result = array(); 264 foreach($res as $r) { 265 foreach($r as $l) { 266 $event->result[$l[0]] = $l[1]; 267 } 268 } 269 } else { 270 # legacy support for old DokuWiki hooks 271 272 // sort into translation slots 273 $res = array(); 274 foreach($event->result as $r) { 275 $tr = $this->helper->getLangPart($r); 276 if(!is_array($res["x$tr"])) $res["x$tr"] = array(); 277 $res["x$tr"][] = $r; 278 } 279 // sort by translations 280 ksort($res); 281 // combine 282 $event->result = array(); 283 foreach($res as $r) { 284 $event->result = array_merge($event->result, $r); 285 } 286 } 287 } 288 289 /** 290 * Redirects to the localized version of the page when showing and browser says so and translation was explicitly requested 291 **/ 292 function translation_redirect_localized() { 293 global $ID; 294 global $conf; 295 global $ACT; 296 297 // redirect to localized page? 298 if( $ACT != 'show' ) { return; } 299 300 $override = isset($_REQUEST['tns']); // override enabled - comes from the bottom bar. 301 $lang = !empty($conf['lang_before_translation']) ? $conf['lang_before_translation'] : $conf['lang']; // Check for original language 302 303 // get current page language - if empty then default; 304 $currentSessionLanguage = $_SESSION[DOKU_COOKIE]['translationcur']; 305 $pageLang = $this->helper->getLangPart($ID); 306 307 if ( empty($pageLang) ) { 308 $pageLang = $lang; 309 } 310 311 // If both match, we're fine. 312 if ( $currentSessionLanguage == $pageLang ) { 313 return; 314 } 315 316 // check current translation 317 if ( empty( $currentSessionLanguage ) && !$override ) { 318 319 // If not set - we must just have entered - set the browser language 320 $currentSessionLanguage = $this->helper->getBrowserLang(); 321 322 // if no browser Language set, take entered namespace language - empty for default. 323 if ( !$currentSessionLanguage ) { 324 $currentSessionLanguage = $pageLang; 325 } 326 327 // Set new Language 328 $_SESSION[DOKU_COOKIE]['translationcur'] = $currentSessionLanguage; 329 330 // Write Language back 331 $pageLang = $currentSessionLanguage; 332 } 333 334 335 if ( $override && $pageLang != $currentSessionLanguage ) { 336 // Set new Language 337 $currentSessionLanguage = $pageLang; 338 $_SESSION[DOKU_COOKIE]['translationcur'] = $currentSessionLanguage; 339 } else if ( !$override ) { 340 // Write Language back 341 $pageLang = $currentSessionLanguage; 342 } 343 344 // If this is the default language, make empty 345 if ( $pageLang == $lang ) { 346 $pageLang = ''; 347 } 348 349 // Generate new Page ID 350 list($newPage,$name) = $this->helper->buildTransID($pageLang,$this->helper->getIDPart($ID)); 351 $newPage = cleanID($newPage); 352 353 // Check if Page exists 354 if ( $newPage != $ID && page_exists($newPage, '', false) ) { 355 // $newPage redirect 356 357 if ( auth_quickaclcheck($newPage) < AUTH_READ ) { return; } 358 359 session_write_close(); 360 $this->_redirect($newPage); 361 } 362 else 363 if ( $override ) { 364 // cleanup redirect 365 session_write_close(); 366 367 if ( auth_quickaclcheck($newPage) < AUTH_READ ) { return; } 368 369 $this->_redirect($ID); 370 } 371 372 // no redirect; 373 } 374 375 376 function _redirect($url) 377 { 378 unset($_GET['id']); 379 $more = array(); 380 381 if ( !empty($_GET) ) { 382 $params = ''; 383 foreach( $_GET as $key => $value ) { 384 // Possible multiple encodings. 385 $more[$key] = $value; 386 } 387 } 388 389 if ( wl( $url, $more, true, '&') != DOKU_URL . substr($_SERVER['REQUEST_URI'], 1) ) { 390 header('Location: ' . wl( $url, $more, true, '&'), 302); 391 exit; 392 } 393 } 394} 395 396//Setup VIM: ex: et ts=4 : 397