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