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