1af1904f9SAndreas Gohr<?php 2af1904f9SAndreas Gohr/** 3af1904f9SAndreas Gohr * Translation Plugin: Simple multilanguage plugin 4af1904f9SAndreas Gohr * 5af1904f9SAndreas Gohr * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6af1904f9SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 7af1904f9SAndreas Gohr */ 8af1904f9SAndreas Gohr 9af1904f9SAndreas Gohr// must be run within Dokuwiki 10af1904f9SAndreas Gohrif(!defined('DOKU_INC')) die(); 11af1904f9SAndreas Gohr 12af1904f9SAndreas Gohrclass helper_plugin_translation extends DokuWiki_Plugin { 13af1904f9SAndreas Gohr var $trans = array(); 14af1904f9SAndreas Gohr var $tns = ''; 157c54a0a6SAndreas Gohr var $defaultlang = ''; 1649a71a89SAndreas Gohr var $LN = array(); // hold native names 17*04971eeaSAndreas Gohr var $opts = array(); // display options 18af1904f9SAndreas Gohr 19af1904f9SAndreas Gohr /** 20af1904f9SAndreas Gohr * Initialize 21af1904f9SAndreas Gohr */ 22af1904f9SAndreas Gohr function helper_plugin_translation(){ 237c54a0a6SAndreas Gohr global $conf; 24af1904f9SAndreas Gohr require_once(DOKU_INC.'inc/pageutils.php'); 25af1904f9SAndreas Gohr require_once(DOKU_INC.'inc/utf8.php'); 26af1904f9SAndreas Gohr 27af1904f9SAndreas Gohr // load wanted translation into array 28af1904f9SAndreas Gohr $this->trans = strtolower(str_replace(',',' ',$this->getConf('translations'))); 29af1904f9SAndreas Gohr $this->trans = array_unique(array_filter(explode(' ',$this->trans))); 30af1904f9SAndreas Gohr sort($this->trans); 317c54a0a6SAndreas Gohr 3249a71a89SAndreas Gohr // load language names 3349a71a89SAndreas Gohr $this->LN = confToHash(dirname(__FILE__).'/lang/langnames.txt'); 3449a71a89SAndreas Gohr 35*04971eeaSAndreas Gohr // display options 36*04971eeaSAndreas Gohr $this->opts = $this->getConf('display'); 37*04971eeaSAndreas Gohr $this->opts = explode(',',$this->opts); 38*04971eeaSAndreas Gohr $this->opts = array_map('trim',$this->opts); 39*04971eeaSAndreas Gohr $this->opts = array_fill_keys($this->opts, true); 40*04971eeaSAndreas Gohr 417c54a0a6SAndreas Gohr // get default translation 427c54a0a6SAndreas Gohr if(!$conf['lang_before_translation']){ 437c54a0a6SAndreas Gohr $dfl = $conf['lang']; 447c54a0a6SAndreas Gohr } else { 457c54a0a6SAndreas Gohr $dfl = $conf['lang_before_translation']; 467c54a0a6SAndreas Gohr } 477c54a0a6SAndreas Gohr if(in_array($dfl,$this->trans)){ 487c54a0a6SAndreas Gohr $this->defaultlang = $dfl; 497c54a0a6SAndreas Gohr }else{ 507c54a0a6SAndreas Gohr $this->defaultlang = ''; 51af1904f9SAndreas Gohr array_unshift($this->trans,''); 527c54a0a6SAndreas Gohr } 537c54a0a6SAndreas Gohr 54af1904f9SAndreas Gohr $this->tns = cleanID($this->getConf('translationns')); 55af1904f9SAndreas Gohr if($this->tns) $this->tns .= ':'; 56af1904f9SAndreas Gohr } 57af1904f9SAndreas Gohr 58af1904f9SAndreas Gohr /** 59af1904f9SAndreas Gohr * Check if the given ID is a translation and return the language code. 60af1904f9SAndreas Gohr */ 61af1904f9SAndreas Gohr function getLangPart($id){ 6226522e09SAndreas Gohr list($lng) = $this->getTransParts($id); 6326522e09SAndreas Gohr return $lng; 64af1904f9SAndreas Gohr } 6526522e09SAndreas Gohr 6626522e09SAndreas Gohr /** 6726522e09SAndreas Gohr * Check if the given ID is a translation and return the language code and 6826522e09SAndreas Gohr * the id part. 6926522e09SAndreas Gohr */ 7026522e09SAndreas Gohr function getTransParts($id){ 7126522e09SAndreas Gohr $rx = '/^'.$this->tns.'('.join('|',$this->trans).'):(.*)/'; 7226522e09SAndreas Gohr if(preg_match($rx,$id,$match)){ 7326522e09SAndreas Gohr return array($match[1],$match[2]); 7426522e09SAndreas Gohr } 7526522e09SAndreas Gohr return array('',$id); 76af1904f9SAndreas Gohr } 77af1904f9SAndreas Gohr 78af1904f9SAndreas Gohr /** 797053cd66SAndreas Gohr * Returns the browser language if it matches with one of the configured 807053cd66SAndreas Gohr * languages 817053cd66SAndreas Gohr */ 827053cd66SAndreas Gohr function getBrowserLang(){ 837053cd66SAndreas Gohr $rx = '/(^|,|:|;|-)('.join('|',$this->trans).')($|,|:|;|-)/i'; 847053cd66SAndreas Gohr if(preg_match($rx,$_SERVER['HTTP_ACCEPT_LANGUAGE'],$match)){ 857053cd66SAndreas Gohr return strtolower($match[2]); 867053cd66SAndreas Gohr } 877053cd66SAndreas Gohr return false; 887053cd66SAndreas Gohr } 897053cd66SAndreas Gohr 907053cd66SAndreas Gohr /** 917c54a0a6SAndreas Gohr * Returns the ID and name to the wanted translation, empty 927c54a0a6SAndreas Gohr * $lng is default lang 93af1904f9SAndreas Gohr */ 94af1904f9SAndreas Gohr function buildTransID($lng,$idpart){ 95af1904f9SAndreas Gohr global $conf; 96af1904f9SAndreas Gohr if($lng){ 97af1904f9SAndreas Gohr $link = ':'.$this->tns.$lng.':'.$idpart; 98af1904f9SAndreas Gohr $name = $lng; 99af1904f9SAndreas Gohr }else{ 100af1904f9SAndreas Gohr $link = ':'.$this->tns.$idpart; 101*04971eeaSAndreas Gohr $name = $this->realLC(''); 102af1904f9SAndreas Gohr } 103af1904f9SAndreas Gohr return array($link,$name); 104af1904f9SAndreas Gohr } 105af1904f9SAndreas Gohr 1061469199dSAndreas Gohr /** 107*04971eeaSAndreas Gohr * Returns the real language code, even when an empty one is given 108*04971eeaSAndreas Gohr * (eg. resolves th default language) 109*04971eeaSAndreas Gohr */ 110*04971eeaSAndreas Gohr function realLC($lc){ 111*04971eeaSAndreas Gohr global $conf; 112*04971eeaSAndreas Gohr if($lc){ 113*04971eeaSAndreas Gohr return $lc; 114*04971eeaSAndreas Gohr }elseif(!$conf['lang_before_translation']){ 115*04971eeaSAndreas Gohr return $conf['lang']; 116*04971eeaSAndreas Gohr } else { 117*04971eeaSAndreas Gohr return $conf['lang_before_translation']; 118*04971eeaSAndreas Gohr } 119*04971eeaSAndreas Gohr } 120*04971eeaSAndreas Gohr 121*04971eeaSAndreas Gohr /** 12284877e9bSAndreas Gohr * Check if current ID should be translated and any GUI 12384877e9bSAndreas Gohr * should be shown 12484877e9bSAndreas Gohr */ 12584877e9bSAndreas Gohr function istranslatable($id,$checkact=true){ 12684877e9bSAndreas Gohr global $ACT; 12784877e9bSAndreas Gohr 12884877e9bSAndreas Gohr if($checkact && $ACT != 'show') return false; 12984877e9bSAndreas Gohr if($this->tns && strpos($id,$this->tns) !== 0) return false; 13084877e9bSAndreas Gohr $skiptrans = trim($this->getConf('skiptrans')); 13184877e9bSAndreas Gohr if($skiptrans && preg_match('/'.$skiptrans.'/ui',':'.$id)) return false; 13284877e9bSAndreas Gohr $meta = p_get_metadata($id); 13384877e9bSAndreas Gohr if($meta['plugin']['translation']['notrans']) return false; 13484877e9bSAndreas Gohr 13584877e9bSAndreas Gohr return true; 13684877e9bSAndreas Gohr } 13784877e9bSAndreas Gohr 13801dd7da9SAndreas Gohr /** 13901dd7da9SAndreas Gohr * Return the (localized) about link 14001dd7da9SAndreas Gohr */ 14101dd7da9SAndreas Gohr function showAbout() { 142c9640767STomasz Tomasik global $ID; 143c9640767STomasz Tomasik global $conf; 144c9640767STomasz Tomasik global $INFO; 145c9640767STomasz Tomasik 146d0bdb959SAndreas Gohr $about = $this->getConf('about'); 147d0bdb959SAndreas Gohr if($this->getConf('localabout')){ 148d0bdb959SAndreas Gohr list($lc,$idpart) = $this->getTransParts($about); 149*04971eeaSAndreas Gohr list($about,$name) = $this->buildTransID($conf['lang'],$idpart); #FIXME conf[lang] is wrong 150d0bdb959SAndreas Gohr $about = cleanID($about); 151d0bdb959SAndreas Gohr } 152c9640767STomasz Tomasik 153c9640767STomasz Tomasik $out = ''; 154c9640767STomasz Tomasik $out .= '<sup>'; 155d0bdb959SAndreas Gohr $out .= html_wikilink($about,'?'); 156c9640767STomasz Tomasik $out .= '</sup>'; 157c9640767STomasz Tomasik 158c9640767STomasz Tomasik return $out; 159c9640767STomasz Tomasik } 160c9640767STomasz Tomasik 16184877e9bSAndreas Gohr /** 1621469199dSAndreas Gohr * Displays the available and configured translations. Needs to be placed in the template. 1631469199dSAndreas Gohr */ 1641469199dSAndreas Gohr function showTranslations(){ 1651469199dSAndreas Gohr global $ID; 1661469199dSAndreas Gohr global $conf; 1671469199dSAndreas Gohr global $INFO; 1681469199dSAndreas Gohr 16984877e9bSAndreas Gohr if(!$this->istranslatable($ID)) return; 17084877e9bSAndreas Gohr $this->checkage(); 1711469199dSAndreas Gohr 172*04971eeaSAndreas Gohr list($lc,$idpart) = $this->getTransParts($ID); 173*04971eeaSAndreas Gohr $lang = $this->realLC($lc); 17439ecab8bSAndreas Gohr 1751469199dSAndreas Gohr 1761469199dSAndreas Gohr $out = '<div class="plugin_translation">'; 177c9640767STomasz Tomasik 178*04971eeaSAndreas Gohr //show title and about 179c9640767STomasz Tomasik if ($this->getConf('description')){ 1801469199dSAndreas Gohr $out .= '<span>'.$this->getLang('translations'); 1818bd452a3SAndreas Gohr if ($this->getConf('about')) $out .= $this->showAbout(); 1821469199dSAndreas Gohr $out .= ':</span> '; 183c9640767STomasz Tomasik } 1841469199dSAndreas Gohr 185*04971eeaSAndreas Gohr // open wrapper 186*04971eeaSAndreas Gohr if($this->getConf('dropdown')){ 187*04971eeaSAndreas Gohr // select needs its own styling 1881469199dSAndreas Gohr if($INFO['exists']){ 1891469199dSAndreas Gohr $class = 'wikilink1'; 1901469199dSAndreas Gohr }else{ 1911469199dSAndreas Gohr $class = 'wikilink2'; 1921469199dSAndreas Gohr } 193*04971eeaSAndreas Gohr if(isset($this->opts['flag'])){ 194*04971eeaSAndreas Gohr $flag = DOKU_BASE.'lib/plugins/translation/flags/'.hsc($lang).'.gif'; 195c9640767STomasz Tomasik } 196c9640767STomasz Tomasik $out .= '<form action="'.wl().'" id="translation__dropdown">'; 197*04971eeaSAndreas Gohr if($flag) $out .= '<img src="'.$flag.'" alt="'.hsc($lang).'" height="11" class="'.$class.'" /> '; 198c9640767STomasz Tomasik $out .= '<select name="id" class="'.$class.'">'; 199*04971eeaSAndreas Gohr }else{ 200*04971eeaSAndreas Gohr $out .= '<ul>'; 201*04971eeaSAndreas Gohr } 202*04971eeaSAndreas Gohr 203*04971eeaSAndreas Gohr // insert items 204*04971eeaSAndreas Gohr foreach($this->trans as $t){ 205*04971eeaSAndreas Gohr $out .= $this->getTransItem($t, $idpart); 206*04971eeaSAndreas Gohr } 207*04971eeaSAndreas Gohr 208*04971eeaSAndreas Gohr // close wrapper 209*04971eeaSAndreas Gohr if($this->getConf('dropdown')){ 2101469199dSAndreas Gohr $out .= '</select>'; 2111469199dSAndreas Gohr $out .= '<input name="go" type="submit" value="→" />'; 2121469199dSAndreas Gohr $out .= '</form>'; 213*04971eeaSAndreas Gohr }else{ 2141469199dSAndreas Gohr $out .= '</ul>'; 2151469199dSAndreas Gohr } 2161469199dSAndreas Gohr 217*04971eeaSAndreas Gohr // show about if not already shown 218*04971eeaSAndreas Gohr if (!$this->getConf('description') && $this->getConf('about')) { 219*04971eeaSAndreas Gohr $out .= ' '; 220*04971eeaSAndreas Gohr $out .= $this->showAbout(); 221*04971eeaSAndreas Gohr } 222*04971eeaSAndreas Gohr 2231469199dSAndreas Gohr $out .= '</div>'; 2241469199dSAndreas Gohr 2251469199dSAndreas Gohr return $out; 2261469199dSAndreas Gohr } 227af1904f9SAndreas Gohr 22801dd7da9SAndreas Gohr /** 22901dd7da9SAndreas Gohr * Create the link or option for a single translation 23001dd7da9SAndreas Gohr * 231*04971eeaSAndreas Gohr * @param $lc string The language code 23201dd7da9SAndreas Gohr * @param $idpart string The ID of the translated page 233*04971eeaSAndreas Gohr * @returns string The item 23401dd7da9SAndreas Gohr */ 235*04971eeaSAndreas Gohr function getTransItem($lc, $idpart) { 236c9640767STomasz Tomasik global $ID; 237c9640767STomasz Tomasik global $conf; 238c9640767STomasz Tomasik 239*04971eeaSAndreas Gohr list($link,$lang) = $this->buildTransID($lc,$idpart); 240c9640767STomasz Tomasik $link = cleanID($link); 241*04971eeaSAndreas Gohr 242*04971eeaSAndreas Gohr 243*04971eeaSAndreas Gohr // class 244c9640767STomasz Tomasik if(page_exists($link,'',false)){ 245c9640767STomasz Tomasik $class = 'wikilink1'; 246c9640767STomasz Tomasik }else{ 247c9640767STomasz Tomasik $class = 'wikilink2'; 248c9640767STomasz Tomasik } 249c9640767STomasz Tomasik 250*04971eeaSAndreas Gohr // local language name 251*04971eeaSAndreas Gohr if ($this->LN[$lang]){ 252*04971eeaSAndreas Gohr $localname = $this->LN[$lang]; 253c9640767STomasz Tomasik } else{ 254*04971eeaSAndreas Gohr $localname = $lang; 255c9640767STomasz Tomasik } 256c9640767STomasz Tomasik 257*04971eeaSAndreas Gohr // current? 258*04971eeaSAndreas Gohr if($ID == $link){ 259*04971eeaSAndreas Gohr $sel = ' selected="selected"'; 260*04971eeaSAndreas Gohr $class .= ' cur'; 261*04971eeaSAndreas Gohr }else{ 262*04971eeaSAndreas Gohr $sel = ''; 263*04971eeaSAndreas Gohr } 264c9640767STomasz Tomasik 265*04971eeaSAndreas Gohr // flag 266*04971eeaSAndreas Gohr if(isset($this->opts['flag'])){ 267*04971eeaSAndreas Gohr $flag = DOKU_BASE.'lib/plugins/translation/flags/'.hsc($lang).'.gif'; 268*04971eeaSAndreas Gohr $style = ' style="background-image: url(\''.$flag.'\')"'; 269*04971eeaSAndreas Gohr $class .= ' flag'; 270*04971eeaSAndreas Gohr } 271*04971eeaSAndreas Gohr 272*04971eeaSAndreas Gohr // what to display as name 273*04971eeaSAndreas Gohr if(isset($this->opts['name'])){ 274*04971eeaSAndreas Gohr $display = hsc($localname); 275*04971eeaSAndreas Gohr if(isset($this->opts['lc'])) $display .= ' ('.hsc($lang).')'; 276*04971eeaSAndreas Gohr }elseif(isset($this->opts['lc'])){ 277*04971eeaSAndreas Gohr $display = hsc($lang); 278*04971eeaSAndreas Gohr }else{ 279*04971eeaSAndreas Gohr $display = ' '; 280*04971eeaSAndreas Gohr } 281*04971eeaSAndreas Gohr 282*04971eeaSAndreas Gohr // prepare output 283*04971eeaSAndreas Gohr $out = ''; 284*04971eeaSAndreas Gohr if($this->getConf('dropdown')){ 285*04971eeaSAndreas Gohr $out .= '<option class="'.$class.'" title="'.hsc($localname).'" value="'.$link.'"'.$sel.$style.'>'; 286*04971eeaSAndreas Gohr $out .= $display; 287*04971eeaSAndreas Gohr $out .= '</option>'; 288*04971eeaSAndreas Gohr }else{ 289c9640767STomasz Tomasik $out .= '<li><div class="li">'; 290*04971eeaSAndreas Gohr $out .= '<a href='.wl($link).' class="'.$class.'" title="'.hsc($localname).'">'; 291*04971eeaSAndreas Gohr if($flag) $out .= '<img src="'.$flag.'" alt="'.hsc($lang).'" height="11" />'; 292*04971eeaSAndreas Gohr $out .= $display; 293c9640767STomasz Tomasik $out .= '</a>'; 294c9640767STomasz Tomasik $out .= '</div></li>'; 295c9640767STomasz Tomasik } 296c9640767STomasz Tomasik 297c9640767STomasz Tomasik return $out; 298c9640767STomasz Tomasik } 299c9640767STomasz Tomasik 30084877e9bSAndreas Gohr /** 30184877e9bSAndreas Gohr * Checks if the current page is a translation of a page 30284877e9bSAndreas Gohr * in the default language. Displays a notice when it is 30384877e9bSAndreas Gohr * older than the original page. Tries to lin to a diff 30484877e9bSAndreas Gohr * with changes on the original since the translation 30584877e9bSAndreas Gohr */ 30684877e9bSAndreas Gohr function checkage(){ 30784877e9bSAndreas Gohr global $ID; 30884877e9bSAndreas Gohr global $INFO; 30984877e9bSAndreas Gohr if(!$this->getConf('checkage')) return; 31084877e9bSAndreas Gohr if(!$INFO['exists']) return; 31184877e9bSAndreas Gohr $lng = $this->getLangPart($ID); 31284877e9bSAndreas Gohr if($lng == $this->defaultlang) return; 313af1904f9SAndreas Gohr 31484877e9bSAndreas Gohr $rx = '/^'.$this->tns.'(('.join('|',$this->trans).'):)?/'; 31584877e9bSAndreas Gohr $idpart = preg_replace($rx,'',$ID); 31684877e9bSAndreas Gohr 31784877e9bSAndreas Gohr // compare modification times 31884877e9bSAndreas Gohr list($orig,$name) = $this->buildTransID($this->defaultlang,$idpart); 31984877e9bSAndreas Gohr $origfn = wikiFN($orig); 32084877e9bSAndreas Gohr if($INFO['lastmod'] >= @filemtime($origfn) ) return; 32184877e9bSAndreas Gohr 32284877e9bSAndreas Gohr // get revision from before translation 32384877e9bSAndreas Gohr $orev = 0; 32484877e9bSAndreas Gohr $revs = getRevisions($orig,0,100); 32584877e9bSAndreas Gohr foreach($revs as $rev){ 32684877e9bSAndreas Gohr if($rev < $INFO['lastmod']){ 32784877e9bSAndreas Gohr $orev = $rev; 32884877e9bSAndreas Gohr break; 32984877e9bSAndreas Gohr } 33084877e9bSAndreas Gohr } 33184877e9bSAndreas Gohr 33244552920SAndreas Gohr // see if the found revision still exists 33344552920SAndreas Gohr if($orev && !page_exists($orig,$orev)) $orev=0; 33444552920SAndreas Gohr 33584877e9bSAndreas Gohr // build the message and display it 336dc3fbdb9SOleksiy Zagorskyi $orig = cleanID($orig); 33784877e9bSAndreas Gohr $msg = sprintf($this->getLang('outdated'),wl($orig)); 33884877e9bSAndreas Gohr if($orev){ 33984877e9bSAndreas Gohr $msg .= sprintf(' '.$this->getLang('diff'), 34084877e9bSAndreas Gohr wl($orig,array('do'=>'diff','rev'=>$orev))); 34184877e9bSAndreas Gohr } 34200431e1eSAndreas Gohr 34300431e1eSAndreas Gohr echo '<div class="notify">'.$msg.'</div>'; 34484877e9bSAndreas Gohr } 345af1904f9SAndreas Gohr} 346