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 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) die(); 11 12class helper_plugin_translation extends DokuWiki_Plugin { 13 var $trans = array(); 14 var $tns = ''; 15 var $defaultlang = ''; 16 var $LN = array(); // hold native names 17 18 /** 19 * Initialize 20 */ 21 function helper_plugin_translation(){ 22 global $conf; 23 require_once(DOKU_INC.'inc/pageutils.php'); 24 require_once(DOKU_INC.'inc/utf8.php'); 25 26 // load wanted translation into array 27 $this->trans = strtolower(str_replace(',',' ',$this->getConf('translations'))); 28 $this->trans = array_unique(array_filter(explode(' ',$this->trans))); 29 sort($this->trans); 30 31 // load language names 32 $this->LN = confToHash(dirname(__FILE__).'/lang/langnames.txt'); 33 34 // get default translation 35 if(!$conf['lang_before_translation']){ 36 $dfl = $conf['lang']; 37 } else { 38 $dfl = $conf['lang_before_translation']; 39 } 40 if(in_array($dfl,$this->trans)){ 41 $this->defaultlang = $dfl; 42 }else{ 43 $this->defaultlang = ''; 44 array_unshift($this->trans,''); 45 } 46 47 48 $this->tns = cleanID($this->getConf('translationns')); 49 if($this->tns) $this->tns .= ':'; 50 } 51 52 /** 53 * Check if the given ID is a translation and return the language code. 54 */ 55 function getLangPart($id){ 56 list($lng) = $this->getTransParts($id); 57 return $lng; 58 } 59 60 /** 61 * Check if the given ID is a translation and return the language code and 62 * the id part. 63 */ 64 function getTransParts($id){ 65 $rx = '/^'.$this->tns.'('.join('|',$this->trans).'):(.*)/'; 66 if(preg_match($rx,$id,$match)){ 67 return array($match[1],$match[2]); 68 } 69 return array('',$id); 70 } 71 72 /** 73 * Returns the browser language if it matches with one of the configured 74 * languages 75 */ 76 function getBrowserLang(){ 77 $rx = '/(^|,|:|;|-)('.join('|',$this->trans).')($|,|:|;|-)/i'; 78 if(preg_match($rx,$_SERVER['HTTP_ACCEPT_LANGUAGE'],$match)){ 79 return strtolower($match[2]); 80 } 81 return false; 82 } 83 84 85 /** 86 * Returns the ID and name to the wanted translation, empty 87 * $lng is default lang 88 */ 89 function buildTransID($lng,$idpart){ 90 global $conf; 91 global $saved_conf; 92 if($lng){ 93 $link = ':'.$this->tns.$lng.':'.$idpart; 94 $name = $lng; 95 }else{ 96 $link = ':'.$this->tns.$idpart; 97 if(!$conf['lang_before_translation']){ 98 $name = $conf['lang']; 99 } else { 100 $name = $conf['lang_before_translation']; 101 } 102 } 103 return array($link,$name); 104 } 105 106 /** 107 * Check if current ID should be translated and any GUI 108 * should be shown 109 */ 110 function istranslatable($id,$checkact=true){ 111 global $ACT; 112 113 if($checkact && $ACT != 'show') return false; 114 if($this->tns && strpos($id,$this->tns) !== 0) return false; 115 $skiptrans = trim($this->getConf('skiptrans')); 116 if($skiptrans && preg_match('/'.$skiptrans.'/ui',':'.$id)) return false; 117 $meta = p_get_metadata($id); 118 if($meta['plugin']['translation']['notrans']) return false; 119 120 return true; 121 } 122 123 /** 124 * Return the (localized) about link 125 * 126 * @fixme why is this doing the detection stuff again? 127 */ 128 function showAbout() { 129 global $ID; 130 global $conf; 131 global $INFO; 132 133 $this->checkage(); 134 135 $LN = confToHash(dirname(__FILE__).'/lang/langnames.txt'); 136 137 $rx = '/^'.$this->tns.'(('.join('|',$this->trans).'):)?/'; 138 $idpart = preg_replace($rx,'',$ID); 139 140 $out = ''; 141 $out .= '<sup>'; 142 if($this->getConf('localabout')){ 143 $lc = ''; 144 145 //try main lang namespace 146 foreach($this->trans as $t){ 147 list($link,$name) = $this->buildTransID($t,$idpart); 148 $link = cleanID($link); 149 if($ID == $link){ 150 $lc = hsc($name); 151 } 152 if ($lc) break; 153 } 154 155 //try browser language 156 if(!$lc) $lc = $this->getBrowserLang(); 157 158 //try wiki language 159 if(!$lc) $lc = $conf['lang']; 160 161 if(!$lc) { //can't find language 162 $localabout = $this->getConf('about'); //http://localhost/dokuwiki/doku.php?id=translation:about 163 } else { //i found language! 164 $localabout = $lc.':'.$this->getConf('about'); //http://localhost/dokuwiki/doku.php?id=en:translation:about 165 } 166 167 //make link 168 $out .= html_wikilink($localabout,'?'); 169 } else { 170 $out .= html_wikilink($this->getConf('about'),'?'); 171 } 172 $out .= '</sup>'; 173 174 return $out; 175 } 176 177 /** 178 * Displays the available and configured translations. Needs to be placed in the template. 179 */ 180 function showTranslations(){ 181 global $ID; 182 global $conf; 183 global $INFO; 184 185 if(!$this->istranslatable($ID)) return; 186 187 $this->checkage(); 188 189 $LN = confToHash(dirname(__FILE__).'/lang/langnames.txt'); 190 191 $rx = '/^'.$this->tns.'(('.join('|',$this->trans).'):)?/'; 192 $idpart = preg_replace($rx,'',$ID); 193 194 $out = '<div class="plugin_translation">'; 195 196 //show text 197 if ($this->getConf('description')){ 198 $out .= '<span>'.$this->getLang('translations'); 199 if ($this->getConf('showabout')) $out .= $this->showAbout(); 200 $out .= ':</span> '; 201 } 202 203 if($this->getConf('dropdown')){ // use dropdown fixme move to own functions 204 if($INFO['exists']){ 205 $class = 'wikilink1'; 206 }else{ 207 $class = 'wikilink2'; 208 } 209 210 $out2 = ""; //FIXME ugly name 211 foreach($this->trans as $t){ 212 list($link,$name) = $this->buildTransID($t,$idpart); 213 $link = cleanID($link); 214 if($ID == $link){ 215 $sel = ' selected="selected"'; 216 if($this->getConf('dropdown2')) { //FIXME ugly name 217 $out .= $this->makecountrylink($LN, $idpart, $t, false); 218 $out .= " "; 219 } 220 }else{ 221 $sel = ''; 222 } 223 if(page_exists($link,'',false)){ 224 $class = 'wikilink1'; 225 }else{ 226 $class = 'wikilink2'; 227 } 228 229 //linktitle 230 $linktitle = ''; 231 if (strlen($LN[$name]) > 0){ 232 $linktitle = $LN[$name]; 233 } else{ 234 $linktitle = hsc($name); 235 } 236 237 $out2 .= '<option value="'.$link.'"'.$sel.' class="'.$class.'" title="'.$linktitle.'">'.hsc($name).'</option>'; 238 } 239 $out .= '<form action="'.wl().'" id="translation__dropdown">'; 240 $out .= '<select name="id" class="'.$class.'">'; 241 $out .= $out2; 242 $out .= '</select>'; 243 $out .= '<input name="go" type="submit" value="→" />'; 244 $out .= '</form>'; 245 246 //link to about (right) 247 if (!$this->getConf('description') && $this->getConf('showabout')) { 248 $out .= ' '; 249 $out .= $this->showAbout(); 250 } 251 }else{ // use list 252 $out .= '<ul>'; 253 254 // FIXME what's this? 255 if (!$this->getConf('description') && $this->getConf('showabout')) { 256 $out .= ' '; 257 $out .= $this->showAbout(); 258 } 259 260 foreach($this->trans as $t){ 261 $out .= $this->makecountrylink($LN, $idpart, $t, true); 262 } 263 $out .= '</ul>'; 264 } 265 266 $out .= '</div>'; 267 268 return $out; 269 } 270 271 /** 272 * Create the link or option for a single translation 273 * 274 * @fixme bad name - translations are not about countries 275 * @param $LN string The language 276 * @param $idpart string The ID of the translated page 277 * @param $t FIXME 278 * @param $div bool true for lists, false for dropdown FIXME 279 * @returns FIXME 280 */ 281 function makecountrylink($LN, $idpart, $t, $div) { 282 global $ID; 283 global $conf; 284 global $INFO; 285 286 require(DOKU_PLUGIN.'translation/flags/langnames.php'); 287 288 list($link,$name) = $this->buildTransID($t,$idpart); 289 $link = cleanID($link); 290 if(page_exists($link,'',false)){ 291 $class = 'wikilink1'; 292 }else{ 293 $class = 'wikilink2'; 294 } 295 296 //linktitle 297 $linktitle = ''; 298 if (strlen($LN[$name]) > 0){ 299 $linktitle = $LN[$name]; 300 } else{ 301 $linktitle = hsc($name); 302 } 303 304 //if (show flag AND ((flag exist) OR (flag not exist AND show blank flag)) 305 if (($langflag[hsc($name)] != NULL && strlen($langflag[hsc($name)]) > 0 && $this->getConf('flags')) || $this->getConf('flags') && $this->getConf('blankflag')) { 306 307 resolve_pageid(getNS($ID),$link,$exists); 308 if ($div) { 309 if ($exists){ //solid box 310 $out .= ' <li><div class="li">'; 311 } else{ //50% transparent box (50% transparent flag) 312 $out .= ' <li><div class="flag_not_exists">'; 313 } 314 } 315 316 //html_wikilink works very slow for images 317 //$flag['title'] = $langname[$name]; 318 //$flag['src'] = DOKU_URL.'lib/plugins/translation/flags/'.$langflag[$name]; 319 //$out .= html_wikilink($link,$flag); 320 321 $out .= '<a href="'.wl($link).'"'; 322 $out .= 'title="'.$linktitle.'"'; 323 //class for image 324 $out .= 'class="wikilink3"'; //FIXME WTF? 325 $out .= '>'; 326 327 //show flag 328 if ($langflag[hsc($name)] != NULL && strlen($langflag[hsc($name)]) > 0){ 329 $out .= '<img src="'.DOKU_URL.'lib/plugins/translation/flags/'.$langflag[hsc($name)].'" alt='.$linktitle.'" border="0">'; 330 } else{ //show blank flag 331 //$out .= '<img src="'.DOKU_BASE.'lib/images/blank.gif'.'" width=15 height=11 alt="'.$linktitle.'" border="0">'; 332 $out .= '<img src="'.DOKU_BASE.'lib/plugins/translation/flags/blankflag.gif'.'" width=15 height=11 alt="'.$linktitle.'" border="0">'; 333 } 334 $out .= '</a>'; 335 336 } else{ //show text (also if flag not exist and blankflag=false) 337 if ($div) { 338 $out .= ' <li><div class="li">'; 339 } 340 $out .= html_wikilink($link,hsc($name)); 341 } 342 if ($div) { 343 $out .= '</div></li>'; 344 } 345 346 return $out; 347 } 348 349 /** 350 * Checks if the current page is a translation of a page 351 * in the default language. Displays a notice when it is 352 * older than the original page. Tries to lin to a diff 353 * with changes on the original since the translation 354 */ 355 function checkage(){ 356 global $ID; 357 global $INFO; 358 if(!$this->getConf('checkage')) return; 359 if(!$INFO['exists']) return; 360 $lng = $this->getLangPart($ID); 361 if($lng == $this->defaultlang) return; 362 363 $rx = '/^'.$this->tns.'(('.join('|',$this->trans).'):)?/'; 364 $idpart = preg_replace($rx,'',$ID); 365 366 // compare modification times 367 list($orig,$name) = $this->buildTransID($this->defaultlang,$idpart); 368 $origfn = wikiFN($orig); 369 if($INFO['lastmod'] >= @filemtime($origfn) ) return; 370 371 // get revision from before translation 372 $orev = 0; 373 $revs = getRevisions($orig,0,100); 374 foreach($revs as $rev){ 375 if($rev < $INFO['lastmod']){ 376 $orev = $rev; 377 break; 378 } 379 } 380 381 // see if the found revision still exists 382 if($orev && !page_exists($orig,$orev)) $orev=0; 383 384 // build the message and display it 385 $orig = cleanID($orig); 386 $msg = sprintf($this->getLang('outdated'),wl($orig)); 387 if($orev){ 388 $msg .= sprintf(' '.$this->getLang('diff'), 389 wl($orig,array('do'=>'diff','rev'=>$orev))); 390 } 391 392 echo '<div class="notify">'.$msg.'</div>'; 393 } 394} 395