diff options
Diffstat (limited to 'platform/www/lib/plugins/translation/helper.php')
-rw-r--r-- | platform/www/lib/plugins/translation/helper.php | 446 |
1 files changed, 446 insertions, 0 deletions
diff --git a/platform/www/lib/plugins/translation/helper.php b/platform/www/lib/plugins/translation/helper.php new file mode 100644 index 0000000..e6bc3b5 --- /dev/null +++ b/platform/www/lib/plugins/translation/helper.php @@ -0,0 +1,446 @@ +<?php +/** + * Translation Plugin: Simple multilanguage plugin + * + * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) + * @author Andreas Gohr <andi@splitbrain.org> + */ + +// must be run within Dokuwiki +if(!defined('DOKU_INC')) die(); + +/** + * Class helper_plugin_translation + */ +class helper_plugin_translation extends DokuWiki_Plugin { + var $translations = array(); + var $translationNs = ''; + var $defaultlang = ''; + var $LN = array(); // hold native names + var $opts = array(); // display options + + /** + * Initialize + */ + function __construct() { + global $conf; + require_once(DOKU_INC . 'inc/pageutils.php'); + require_once(DOKU_INC . 'inc/utf8.php'); + + $this->loadTranslationNamespaces(); + + // load language names + $this->LN = confToHash(dirname(__FILE__) . '/lang/langnames.txt'); + + // display options + $this->opts = $this->getConf('display'); + $this->opts = explode(',', $this->opts); + $this->opts = array_map('trim', $this->opts); + $this->opts = array_fill_keys($this->opts, true); + + // get default translation + if(!empty($conf['lang_before_translation'])) { + $dfl = $conf['lang']; + } else { + $dfl = $conf['lang_before_translation']; + } + if(in_array($dfl, $this->translations)) { + $this->defaultlang = $dfl; + } else { + $this->defaultlang = ''; + array_unshift($this->translations, ''); + } + + $this->translationNs = cleanID($this->getConf('translationns')); + if($this->translationNs) $this->translationNs .= ':'; + } + + /** + * Parse 'translations'-setting into $this->translations + */ + public function loadTranslationNamespaces() { + // load wanted translation into array + $this->translations = strtolower(str_replace(',', ' ', $this->getConf('translations'))); + $this->translations = array_unique(array_filter(explode(' ', $this->translations))); + sort($this->translations); + } + + /** + * Check if the given ID is a translation and return the language code. + * + * @param string $id + * @return string + */ + function getLangPart($id) { + list($lng) = $this->getTransParts($id); + return $lng; + } + + /** + * Check if the given ID is a translation and return the language code and + * the id part. + * + * @param string $id + * @return array + */ + function getTransParts($id) { + $rx = '/^' . $this->translationNs . '(' . join('|', $this->translations) . '):(.*)/'; + if(preg_match($rx, $id, $match)) { + return array($match[1], $match[2]); + } + return array('', $id); + } + + /** + * Returns the browser language if it matches with one of the configured + * languages + */ + function getBrowserLang() { + global $conf; + $langs = $this->translations; + if (!in_array($conf['lang'], $langs)) { + $langs[] = $conf['lang']; + } + $rx = '/(^|,|:|;|-)(' . join('|', $langs) . ')($|,|:|;|-)/i'; + if(preg_match($rx, $_SERVER['HTTP_ACCEPT_LANGUAGE'], $match)) { + return strtolower($match[2]); + } + return false; + } + + /** + * Returns the ID and name to the wanted translation, empty + * $lng is default lang + * + * @param string $lng + * @param string $idpart + * @return array + */ + function buildTransID($lng, $idpart) { + if($lng && in_array($lng, $this->translations)) { + $link = ':' . $this->translationNs . $lng . ':' . $idpart; + $name = $lng; + } else { + $link = ':' . $this->translationNs . $idpart; + $name = $this->realLC(''); + } + return array($link, $name); + } + + /** + * Returns the real language code, even when an empty one is given + * (eg. resolves th default language) + * + * @param string $lc + * @return string + */ + function realLC($lc) { + global $conf; + if($lc) { + return $lc; + } elseif(!$conf['lang_before_translation']) { + return $conf['lang']; + } else { + return $conf['lang_before_translation']; + } + } + + /** + * Check if current ID should be translated and any GUI + * should be shown + * + * @param string $id + * @param bool $checkact + * @return bool + */ + function istranslatable($id, $checkact = true) { + global $ACT; + + if($checkact && $ACT != 'show') return false; + if($this->translationNs && strpos($id, $this->translationNs) !== 0) return false; + $skiptrans = trim($this->getConf('skiptrans')); + if($skiptrans && preg_match('/' . $skiptrans . '/ui', ':' . $id)) return false; + $meta = p_get_metadata($id); + if(!empty($meta['plugin']['translation']['notrans'])) return false; + + return true; + } + + /** + * Return the (localized) about link + */ + function showAbout() { + global $ID; + + $curlc = $this->getLangPart($ID); + + $about = $this->getConf('about'); + if($this->getConf('localabout')) { + list(/* $lc */, $idpart) = $this->getTransParts($about); + list($about, /* $name */) = $this->buildTransID($curlc, $idpart); + $about = cleanID($about); + } + + $out = ''; + $out .= '<sup>'; + $out .= html_wikilink($about, '?'); + $out .= '</sup>'; + + return $out; + } + + /** + * Returns a list of (lc => link) for all existing translations of a page + * + * @param $id + * @return array + */ + function getAvailableTranslations($id) { + $result = array(); + + list($lc, $idpart) = $this->getTransParts($id); + + foreach($this->translations as $t) { + if($t == $lc) continue; //skip self + list($link, $name) = $this->buildTransID($t, $idpart); + if(page_exists($link)) { + $result[$name] = $link; + } + } + + return $result; + } + + /** + * Creates an UI for linking to the available and configured translations + * + * Can be called from the template or via the ~~TRANS~~ syntax component. + */ + public function showTranslations() { + global $conf; + global $INFO; + + if(!$this->istranslatable($INFO['id'])) return ''; + $this->checkage(); + + list($lc, $idpart) = $this->getTransParts($INFO['id']); + $lang = $this->realLC($lc); + + $out = '<div class="plugin_translation">'; + + //show title and about + if(isset($this->opts['title'])) { + $out .= '<span>' . $this->getLang('translations'); + if($this->getConf('about')) $out .= $this->showAbout(); + $out .= ':</span> '; + if(isset($this->opts['twolines'])) $out .= '<br />'; + } + + // open wrapper + if($this->getConf('dropdown')) { + // select needs its own styling + if($INFO['exists']) { + $class = 'wikilink1'; + } else { + $class = 'wikilink2'; + } + if(isset($this->opts['flag'])) { + $flag = DOKU_BASE . 'lib/plugins/translation/flags/' . hsc($lang) . '.gif'; + }else{ + $flag = ''; + } + + if($conf['userewrite']) { + $action = wl(); + } else { + $action = script(); + } + + $out .= '<form action="' . $action . '" id="translation__dropdown">'; + if($flag) $out .= '<img src="' . $flag . '" alt="' . hsc($lang) . '" height="11" class="' . $class . '" /> '; + $out .= '<select name="id" class="' . $class . '">'; + } else { + $out .= '<ul>'; + } + + // insert items + + array_shift($this->translations); + foreach($this->translations as $t) { + $out .= $this->getTransItem($t, $idpart); + } + + // close wrapper + if($this->getConf('dropdown')) { + $out .= '</select>'; + $out .= '<input name="go" type="submit" value="→" />'; + $out .= '</form>'; + } else { + $out .= '</ul>'; + } + + // show about if not already shown + if(!isset($this->opts['title']) && $this->getConf('about')) { + $out .= ' '; + $out .= $this->showAbout(); + } + + $out .= '</div>'; + + return $out; + } + + /** + * Return the local name + * + * @param $lang + * @return string + */ + function getLocalName($lang) { + if($this->LN[$lang]) { + return $this->LN[$lang]; + } + return $lang; + } + + /** + * Create the link or option for a single translation + * + * @param $lc string The language code + * @param $idpart string The ID of the translated page + * @returns string The item + */ + function getTransItem($lc, $idpart) { + global $ID; + global $conf; + + list($link, $lang) = $this->buildTransID($lc, $idpart); + $link = cleanID($link); + + // class + if(page_exists($link, '', false)) { + $class = 'wikilink1'; + } else { + $class = 'wikilink2'; + } + + // local language name + $localname = $this->getLocalName($lang); + + $divClass = 'li'; + // current? + if($ID == $link) { + $sel = ' selected="selected"'; + $class .= ' cur'; + $divClass .= ' cur'; + } else { + $sel = ''; + } + + // flag + $flag = false; + $style = ''; + if(isset($this->opts['flag'])) { + $flag = DOKU_BASE . 'lib/plugins/translation/flags/' . hsc($lang) . '.gif'; + $style = ' style="background-image: url(\'' . $flag . '\')"'; + $class .= ' flag'; + } + + // what to display as name + if(isset($this->opts['name'])) { + $display = hsc($localname); + if(isset($this->opts['langcode'])) $display .= ' (' . hsc($lang) . ')'; + } elseif(isset($this->opts['langcode'])) { + $display = hsc($lang); + } else { + $display = ' '; + } + + // prepare output + $out = ''; + if($this->getConf('dropdown')) { + if($conf['useslash']) $link = str_replace(':', '/', $link); + + $out .= '<option class="' . $class . '" title="' . hsc($localname) . '" value="' . $link . '"' . $sel . $style . '>'; + $out .= $display; + $out .= '</option>'; + } else { + $out .= "<li><div class='$divClass'>"; + $out .= '<a href="' . wl($link) . '" class="' . $class . '" title="' . hsc($localname) . '">'; + if($flag) $out .= '<img src="' . $flag . '" alt="' . hsc($lang) . '" height="11" />'; + $out .= $display; + $out .= '</a>'; + $out .= '</div></li>'; + } + + return $out; + } + + /** + * Checks if the current page is a translation of a page + * in the default language. Displays a notice when it is + * older than the original page. Tries to link to a diff + * with changes on the original since the translation + */ + function checkage() { + global $ID; + global $INFO; + if(!$this->getConf('checkage')) return; + if(!$INFO['exists']) return; + $lng = $this->getLangPart($ID); + if($lng == $this->defaultlang) return; + + $rx = '/^' . $this->translationNs . '((' . join('|', $this->translations) . '):)?/'; + $idpart = preg_replace($rx, '', $ID); + + // compare modification times + list($orig, /* $name */) = $this->buildTransID($this->defaultlang, $idpart); + $origfn = wikiFN($orig); + if($INFO['lastmod'] >= @filemtime($origfn)) return; + + // get revision from before translation + $orev = 0; + + $changelog = new PageChangelog($orig); + $revs = $changelog->getRevisions(0, 100); + foreach($revs as $rev) { + if($rev < $INFO['lastmod']) { + $orev = $rev; + break; + } + } + + // see if the found revision still exists + if($orev && !page_exists($orig, $orev)) $orev = 0; + + // build the message and display it + $orig = cleanID($orig); + $msg = sprintf($this->getLang('outdated'), wl($orig)); + + $difflink = $this->getOldDiffLink($orig, $INFO['lastmod']); + if ($difflink) { + $msg .= sprintf(' ' . $this->getLang('diff'), $difflink); + } + + echo '<div class="notify">' . $msg . '</div>'; + } + + function getOldDiffLink($id, $lastmod) { + // get revision from before translation + $orev = false; + $changelog = new PageChangelog($id); + $revs = $changelog->getRevisions(0, 100); + foreach($revs as $rev) { + if($rev < $lastmod) { + $orev = $rev; + break; + } + } + if($orev && !page_exists($id, $orev)) { + return false; + } + $id = cleanID($id); + return wl($id, array('do' => 'diff', 'rev' => $orev)); + + } +} |