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 global $ID; 23 global $JSINFO; 24 25 require_once(DOKU_INC.'inc/pageutils.php'); 26 require_once(DOKU_INC.'inc/utf8.php'); 27 28 // load wanted translation into array 29 $this->trans = strtolower(str_replace(',',' ',$this->getConf('translations'))); 30 $this->trans = array_unique(array_filter(explode(' ',$this->trans))); 31 sort($this->trans); 32 33 // get default translation 34 if(!$conf['lang_before_translation']){ 35 $dfl = $conf['lang']; 36 } else { 37 $dfl = $conf['lang_before_translation']; 38 } 39 if(in_array($dfl,$this->trans)){ 40 $this->defaultlang = $dfl; 41 }else{ 42 $this->defaultlang = ''; 43 array_unshift($this->trans,''); 44 } 45 46 47 $this->tns = $this->setupTNS($ID); 48 if ( $this->tns === false ) { return false; } 49 $JSINFO['conf']['lang'] = $dfl; 50 } 51 52 53 // Inner function for sorting 54 private function lensort($a,$b){ 55 return strlen($b)-strlen($a); 56 } 57 58 function setupTNS($ID) { 59 60 if ( !empty( $this->tns) ) { return $this->tns; } 61 if ( empty($ID) ) { $ID = getID(); } 62 63 $tnsA = explode(' ', $this->getConf('translationns')); 64 if ( empty($tnsA) ) return ''; // there is just this one - and translation is active. 65 66 usort($tnsA,array($this, 'lensort') ); 67 foreach ( $tnsA as $tns ) { 68 $tns = cleanID(trim($tns)); 69 if(substr($tns, -1) != ':') { $tns .= ':'; } 70 if(strpos($ID,$tns) === false) continue; 71 72 return $tns; 73 } 74 75 return false; 76 } 77 78 /** 79 * Check if the given ID is a translation and return the language code. 80 */ 81 function getLangPart($id){ 82 $rx = '/^'.$this->tns.'('.join('|',$this->trans).'):/'; 83 if(preg_match($rx,$id,$match)){ 84 return $match[1]; 85 } 86 return ''; 87 } 88 89 /** 90 * Returns the browser language if it matches with one of the configured 91 * languages 92 */ 93 function getBrowserLang(){ 94 $rx = '/(^|,|:|;|-)('.join('|',$this->trans).')($|,|:|;|-)/i'; 95 if(preg_match($rx,$_SERVER['HTTP_ACCEPT_LANGUAGE'],$match)){ 96 return strtolower($match[2]); 97 } 98 return false; 99 } 100 101 102 /** 103 * Returns the ID and name to the wanted translation, empty 104 * $lng is default lang 105 */ 106 function buildTransID($lng,$idpart){ 107 global $conf; 108 global $saved_conf; 109 if($lng){ 110 $link = ':'.$this->tns.$lng.':'.$idpart; 111 $name = $lng; 112 }else{ 113 $link = ':'.$this->tns.$idpart; 114 if(!$conf['lang_before_translation']){ 115 $name = $conf['lang']; 116 } else { 117 $name = $conf['lang_before_translation']; 118 } 119 } 120 return array($link,$name); 121 } 122 123 /** 124 * Check if current ID should be translated and any GUI 125 * should be shown 126 */ 127 function istranslatable($id,$checkact=true){ 128 global $ACT; 129 130 if($checkact && $ACT != 'show') return false; 131 if($this->tns && strpos($id,$this->tns) !== 0) return false; 132 $skiptrans = trim($this->getConf('skiptrans')); 133 if($skiptrans && preg_match('/'.$skiptrans.'/ui',':'.$id)) return false; 134 $meta = p_get_metadata($id); 135 if($meta['plugin']['translation']['notrans']) return false; 136 137 return true; 138 } 139 140 /** 141 * Displays the available and configured translations. Needs to be placed in the template. 142 */ 143 function showTranslations(){ 144 global $ID; 145 global $conf; 146 global $INFO; 147 148 $this->tns = $this->setupTNS($ID); 149 if(!$this->istranslatable($ID)) return; 150 $hasTranslation = false; 151 152 $this->checkage(); 153 154 $LN = confToHash(dirname(__FILE__).'/lang/langnames.txt'); 155 156 $rx = '/^'.$this->tns.'(('.join('|',$this->trans).'):)?/'; 157 $idpart = preg_replace($rx,'',$ID); 158 159 $out = '<div class="plugin_translation">'; 160 $out .= '<span>'.$this->getLang('translations'); 161 if($this->getConf('about')){ 162 $out .= '<sup>'.html_wikilink($this->getConf('about'),'?').'</sup>'; 163 } 164 $out .= ':</span> '; 165 166 if($this->getConf('dropdown')){ // use dropdown 167 if($INFO['exists']){ 168 $class = 'wikilink1'; 169 }else{ 170 $class = 'wikilink2'; 171 } 172 $out .= '<form action="'.wl().'" id="translation__dropdown">'; 173 $out .= '<select name="id" class="'.$class.'">'; 174 foreach($this->trans as $t){ 175 list($link,$name) = $this->buildTransID($t,$idpart); 176 $link = cleanID($link); 177 178 if ( auth_quickaclcheck($link) < AUTH_READ ) { continue; } 179 if ( !$hasTranslation && $t != $conf['lang'] && $t != $conf['lang_before_translation'] ) { $hasTranslation = true; } 180 181 if($ID == $link){ 182 $sel = ' selected="selected"'; 183 }else{ 184 $sel = ''; 185 } 186 if(page_exists($link,'',false)){ 187 $class = 'wikilink1'; 188 }else{ 189 $class = 'wikilink2'; 190 } 191 $out .= '<option value="'.$link.'"'.$sel.' class="'.$class.'" title="'.$LN[$name].'">'.hsc($name).'</option>'; 192 } 193 $out .= '</select>'; 194 $out .= '<input name="go" type="submit" value="→" />'; 195 $out .= '</form>'; 196 }else{ // use list 197 $out .= '<ul>'; 198 foreach($this->trans as $t){ 199 list($link,$name) = $this->buildTransID($t,$idpart); 200 201 $link = cleanID($link); 202 203 if ( auth_quickaclcheck($link) < AUTH_READ ) { continue; } 204 if ( !$hasTranslation && !empty($t) ) { $hasTranslation = true; } 205 206 if(page_exists($link,'',false)){ 207 $class = 'wikilink1'; 208 }else{ 209 $class = 'wikilink2'; 210 } 211 212 $out .= ' <li><div class="li"><a href="'.wl($link, 's=translation_true').'" class="'.$class.'" title="'.$LN[$name].'">'.hsc($name).'</a></div></li>'; 213 } 214 $out .= '</ul>'; 215 } 216 217 $out .= '</div>'; 218 219 return $hasTranslation ? $out : ''; 220 } 221 222 /** 223 * Checks if the current page is a translation of a page 224 * in the default language. Displays a notice when it is 225 * older than the original page. Tries to lin to a diff 226 * with changes on the original since the translation 227 */ 228 function checkage(){ 229 global $ID; 230 global $INFO; 231 if(!$this->getConf('checkage')) return; 232 if(!$INFO['exists']) return; 233 $lng = $this->getLangPart($ID); 234 if($lng == $this->defaultlang) return; 235 236 $rx = '/^'.$this->tns.'(('.join('|',$this->trans).'):)?/'; 237 $idpart = preg_replace($rx,'',$ID); 238 239 // compare modification times 240 list($orig,$name) = $this->buildTransID($this->defaultlang,$idpart); 241 $origfn = wikiFN($orig); 242 if($INFO['lastmod'] >= @filemtime($origfn) ) return; 243 244 // get revision from before translation 245 $orev = 0; 246 $revs = getRevisions($orig,0,100); 247 foreach($revs as $rev){ 248 if($rev < $INFO['lastmod']){ 249 $orev = $rev; 250 break; 251 } 252 } 253 254 // see if the found revision still exists 255 if($orev && !page_exists($orig,$orev)) $orev=0; 256 257 // build the message and display it 258 $msg = sprintf($this->getLang('outdated'),wl($orig)); 259 if($orev){ 260 $msg .= sprintf(' '.$this->getLang('diff'), 261 wl($orig,array('do'=>'diff','rev'=>$orev))); 262 } 263 264 echo '<div class="notify">'.$msg.'</div>'; 265 } 266} 267