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