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