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 1704971eeaSAndreas 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 3504971eeaSAndreas Gohr // display options 3604971eeaSAndreas Gohr $this->opts = $this->getConf('display'); 3704971eeaSAndreas Gohr $this->opts = explode(',',$this->opts); 3804971eeaSAndreas Gohr $this->opts = array_map('trim',$this->opts); 3904971eeaSAndreas Gohr $this->opts = array_fill_keys($this->opts, true); 4004971eeaSAndreas 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; 10104971eeaSAndreas Gohr $name = $this->realLC(''); 102af1904f9SAndreas Gohr } 103af1904f9SAndreas Gohr return array($link,$name); 104af1904f9SAndreas Gohr } 105af1904f9SAndreas Gohr 1061469199dSAndreas Gohr /** 10704971eeaSAndreas Gohr * Returns the real language code, even when an empty one is given 10804971eeaSAndreas Gohr * (eg. resolves th default language) 10904971eeaSAndreas Gohr */ 11004971eeaSAndreas Gohr function realLC($lc){ 11104971eeaSAndreas Gohr global $conf; 11204971eeaSAndreas Gohr if($lc){ 11304971eeaSAndreas Gohr return $lc; 11404971eeaSAndreas Gohr }elseif(!$conf['lang_before_translation']){ 11504971eeaSAndreas Gohr return $conf['lang']; 11604971eeaSAndreas Gohr } else { 11704971eeaSAndreas Gohr return $conf['lang_before_translation']; 11804971eeaSAndreas Gohr } 11904971eeaSAndreas Gohr } 12004971eeaSAndreas Gohr 12104971eeaSAndreas 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 146*5ad1c278SAndreas Gohr $curlc = $this->getLangPart($ID); 147f34c9eb2SAndreas Gohr 148d0bdb959SAndreas Gohr $about = $this->getConf('about'); 149d0bdb959SAndreas Gohr if($this->getConf('localabout')){ 150d0bdb959SAndreas Gohr list($lc,$idpart) = $this->getTransParts($about); 151f34c9eb2SAndreas Gohr list($about,$name) = $this->buildTransID($curlc,$idpart); 152d0bdb959SAndreas Gohr $about = cleanID($about); 153d0bdb959SAndreas Gohr } 154c9640767STomasz Tomasik 155c9640767STomasz Tomasik $out = ''; 156c9640767STomasz Tomasik $out .= '<sup>'; 157d0bdb959SAndreas Gohr $out .= html_wikilink($about,'?'); 158c9640767STomasz Tomasik $out .= '</sup>'; 159c9640767STomasz Tomasik 160c9640767STomasz Tomasik return $out; 161c9640767STomasz Tomasik } 162c9640767STomasz Tomasik 16384877e9bSAndreas Gohr /** 1641469199dSAndreas Gohr * Displays the available and configured translations. Needs to be placed in the template. 1651469199dSAndreas Gohr */ 1661469199dSAndreas Gohr function showTranslations(){ 1671469199dSAndreas Gohr global $ID; 1681469199dSAndreas Gohr global $conf; 1691469199dSAndreas Gohr global $INFO; 1701469199dSAndreas Gohr 17184877e9bSAndreas Gohr if(!$this->istranslatable($ID)) return; 17284877e9bSAndreas Gohr $this->checkage(); 1731469199dSAndreas Gohr 17404971eeaSAndreas Gohr list($lc,$idpart) = $this->getTransParts($ID); 17504971eeaSAndreas Gohr $lang = $this->realLC($lc); 17639ecab8bSAndreas Gohr 1771469199dSAndreas Gohr 1781469199dSAndreas Gohr $out = '<div class="plugin_translation">'; 179c9640767STomasz Tomasik 18004971eeaSAndreas Gohr //show title and about 181c730e7ddSAndreas Gohr if (isset($this->opts['title'])){ 1821469199dSAndreas Gohr $out .= '<span>'.$this->getLang('translations'); 1838bd452a3SAndreas Gohr if ($this->getConf('about')) $out .= $this->showAbout(); 1841469199dSAndreas Gohr $out .= ':</span> '; 185c730e7ddSAndreas Gohr if(isset($this->opts['twolines'])) $out .= '<br />'; 186c9640767STomasz Tomasik } 1871469199dSAndreas Gohr 18804971eeaSAndreas Gohr // open wrapper 18904971eeaSAndreas Gohr if($this->getConf('dropdown')){ 19004971eeaSAndreas Gohr // select needs its own styling 1911469199dSAndreas Gohr if($INFO['exists']){ 1921469199dSAndreas Gohr $class = 'wikilink1'; 1931469199dSAndreas Gohr }else{ 1941469199dSAndreas Gohr $class = 'wikilink2'; 1951469199dSAndreas Gohr } 19604971eeaSAndreas Gohr if(isset($this->opts['flag'])){ 19704971eeaSAndreas Gohr $flag = DOKU_BASE.'lib/plugins/translation/flags/'.hsc($lang).'.gif'; 198c9640767STomasz Tomasik } 199c9640767STomasz Tomasik $out .= '<form action="'.wl().'" id="translation__dropdown">'; 20004971eeaSAndreas Gohr if($flag) $out .= '<img src="'.$flag.'" alt="'.hsc($lang).'" height="11" class="'.$class.'" /> '; 201c9640767STomasz Tomasik $out .= '<select name="id" class="'.$class.'">'; 20204971eeaSAndreas Gohr }else{ 20304971eeaSAndreas Gohr $out .= '<ul>'; 20404971eeaSAndreas Gohr } 20504971eeaSAndreas Gohr 20604971eeaSAndreas Gohr // insert items 20704971eeaSAndreas Gohr foreach($this->trans as $t){ 20804971eeaSAndreas Gohr $out .= $this->getTransItem($t, $idpart); 20904971eeaSAndreas Gohr } 21004971eeaSAndreas Gohr 21104971eeaSAndreas Gohr // close wrapper 21204971eeaSAndreas Gohr if($this->getConf('dropdown')){ 2131469199dSAndreas Gohr $out .= '</select>'; 2141469199dSAndreas Gohr $out .= '<input name="go" type="submit" value="→" />'; 2151469199dSAndreas Gohr $out .= '</form>'; 21604971eeaSAndreas Gohr }else{ 2171469199dSAndreas Gohr $out .= '</ul>'; 2181469199dSAndreas Gohr } 2191469199dSAndreas Gohr 22004971eeaSAndreas Gohr // show about if not already shown 221c730e7ddSAndreas Gohr if (!isset($this->opts['title']) && $this->getConf('about')) { 22204971eeaSAndreas Gohr $out .= ' '; 22304971eeaSAndreas Gohr $out .= $this->showAbout(); 22404971eeaSAndreas Gohr } 22504971eeaSAndreas Gohr 2261469199dSAndreas Gohr $out .= '</div>'; 2271469199dSAndreas Gohr 2281469199dSAndreas Gohr return $out; 2291469199dSAndreas Gohr } 230af1904f9SAndreas Gohr 23101dd7da9SAndreas Gohr /** 23201dd7da9SAndreas Gohr * Create the link or option for a single translation 23301dd7da9SAndreas Gohr * 23404971eeaSAndreas Gohr * @param $lc string The language code 23501dd7da9SAndreas Gohr * @param $idpart string The ID of the translated page 23604971eeaSAndreas Gohr * @returns string The item 23701dd7da9SAndreas Gohr */ 23804971eeaSAndreas Gohr function getTransItem($lc, $idpart) { 239c9640767STomasz Tomasik global $ID; 240c9640767STomasz Tomasik global $conf; 241c9640767STomasz Tomasik 24204971eeaSAndreas Gohr list($link,$lang) = $this->buildTransID($lc,$idpart); 243c9640767STomasz Tomasik $link = cleanID($link); 24404971eeaSAndreas Gohr 24504971eeaSAndreas Gohr 24604971eeaSAndreas Gohr // class 247c9640767STomasz Tomasik if(page_exists($link,'',false)){ 248c9640767STomasz Tomasik $class = 'wikilink1'; 249c9640767STomasz Tomasik }else{ 250c9640767STomasz Tomasik $class = 'wikilink2'; 251c9640767STomasz Tomasik } 252c9640767STomasz Tomasik 25304971eeaSAndreas Gohr // local language name 25404971eeaSAndreas Gohr if ($this->LN[$lang]){ 25504971eeaSAndreas Gohr $localname = $this->LN[$lang]; 256c9640767STomasz Tomasik } else{ 25704971eeaSAndreas Gohr $localname = $lang; 258c9640767STomasz Tomasik } 259c9640767STomasz Tomasik 26004971eeaSAndreas Gohr // current? 26104971eeaSAndreas Gohr if($ID == $link){ 26204971eeaSAndreas Gohr $sel = ' selected="selected"'; 26304971eeaSAndreas Gohr $class .= ' cur'; 26404971eeaSAndreas Gohr }else{ 26504971eeaSAndreas Gohr $sel = ''; 26604971eeaSAndreas Gohr } 267c9640767STomasz Tomasik 26804971eeaSAndreas Gohr // flag 26904971eeaSAndreas Gohr if(isset($this->opts['flag'])){ 27004971eeaSAndreas Gohr $flag = DOKU_BASE.'lib/plugins/translation/flags/'.hsc($lang).'.gif'; 27104971eeaSAndreas Gohr $style = ' style="background-image: url(\''.$flag.'\')"'; 27204971eeaSAndreas Gohr $class .= ' flag'; 27304971eeaSAndreas Gohr } 27404971eeaSAndreas Gohr 27504971eeaSAndreas Gohr // what to display as name 27604971eeaSAndreas Gohr if(isset($this->opts['name'])){ 27704971eeaSAndreas Gohr $display = hsc($localname); 278c730e7ddSAndreas Gohr if(isset($this->opts['langcode'])) $display .= ' ('.hsc($lang).')'; 279c730e7ddSAndreas Gohr }elseif(isset($this->opts['langcode'])){ 28004971eeaSAndreas Gohr $display = hsc($lang); 28104971eeaSAndreas Gohr }else{ 28204971eeaSAndreas Gohr $display = ' '; 28304971eeaSAndreas Gohr } 28404971eeaSAndreas Gohr 28504971eeaSAndreas Gohr // prepare output 28604971eeaSAndreas Gohr $out = ''; 28704971eeaSAndreas Gohr if($this->getConf('dropdown')){ 28804971eeaSAndreas Gohr $out .= '<option class="'.$class.'" title="'.hsc($localname).'" value="'.$link.'"'.$sel.$style.'>'; 28904971eeaSAndreas Gohr $out .= $display; 29004971eeaSAndreas Gohr $out .= '</option>'; 29104971eeaSAndreas Gohr }else{ 292c9640767STomasz Tomasik $out .= '<li><div class="li">'; 29304971eeaSAndreas Gohr $out .= '<a href='.wl($link).' class="'.$class.'" title="'.hsc($localname).'">'; 29404971eeaSAndreas Gohr if($flag) $out .= '<img src="'.$flag.'" alt="'.hsc($lang).'" height="11" />'; 29504971eeaSAndreas Gohr $out .= $display; 296c9640767STomasz Tomasik $out .= '</a>'; 297c9640767STomasz Tomasik $out .= '</div></li>'; 298c9640767STomasz Tomasik } 299c9640767STomasz Tomasik 300c9640767STomasz Tomasik return $out; 301c9640767STomasz Tomasik } 302c9640767STomasz Tomasik 30384877e9bSAndreas Gohr /** 30484877e9bSAndreas Gohr * Checks if the current page is a translation of a page 30584877e9bSAndreas Gohr * in the default language. Displays a notice when it is 30684877e9bSAndreas Gohr * older than the original page. Tries to lin to a diff 30784877e9bSAndreas Gohr * with changes on the original since the translation 30884877e9bSAndreas Gohr */ 30984877e9bSAndreas Gohr function checkage(){ 31084877e9bSAndreas Gohr global $ID; 31184877e9bSAndreas Gohr global $INFO; 31284877e9bSAndreas Gohr if(!$this->getConf('checkage')) return; 31384877e9bSAndreas Gohr if(!$INFO['exists']) return; 31484877e9bSAndreas Gohr $lng = $this->getLangPart($ID); 31584877e9bSAndreas Gohr if($lng == $this->defaultlang) return; 316af1904f9SAndreas Gohr 31784877e9bSAndreas Gohr $rx = '/^'.$this->tns.'(('.join('|',$this->trans).'):)?/'; 31884877e9bSAndreas Gohr $idpart = preg_replace($rx,'',$ID); 31984877e9bSAndreas Gohr 32084877e9bSAndreas Gohr // compare modification times 32184877e9bSAndreas Gohr list($orig,$name) = $this->buildTransID($this->defaultlang,$idpart); 32284877e9bSAndreas Gohr $origfn = wikiFN($orig); 32384877e9bSAndreas Gohr if($INFO['lastmod'] >= @filemtime($origfn) ) return; 32484877e9bSAndreas Gohr 32584877e9bSAndreas Gohr // get revision from before translation 32684877e9bSAndreas Gohr $orev = 0; 32784877e9bSAndreas Gohr $revs = getRevisions($orig,0,100); 32884877e9bSAndreas Gohr foreach($revs as $rev){ 32984877e9bSAndreas Gohr if($rev < $INFO['lastmod']){ 33084877e9bSAndreas Gohr $orev = $rev; 33184877e9bSAndreas Gohr break; 33284877e9bSAndreas Gohr } 33384877e9bSAndreas Gohr } 33484877e9bSAndreas Gohr 33544552920SAndreas Gohr // see if the found revision still exists 33644552920SAndreas Gohr if($orev && !page_exists($orig,$orev)) $orev=0; 33744552920SAndreas Gohr 33884877e9bSAndreas Gohr // build the message and display it 339dc3fbdb9SOleksiy Zagorskyi $orig = cleanID($orig); 34084877e9bSAndreas Gohr $msg = sprintf($this->getLang('outdated'),wl($orig)); 34184877e9bSAndreas Gohr if($orev){ 34284877e9bSAndreas Gohr $msg .= sprintf(' '.$this->getLang('diff'), 34384877e9bSAndreas Gohr wl($orig,array('do'=>'diff','rev'=>$orev))); 34484877e9bSAndreas Gohr } 34500431e1eSAndreas Gohr 34600431e1eSAndreas Gohr echo '<div class="notify">'.$msg.'</div>'; 34784877e9bSAndreas Gohr } 348af1904f9SAndreas Gohr} 349