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