*/ // must be run within Dokuwiki if (!defined('DOKU_INC')) die(); if (!defined('DOKU_LF')) define ('DOKU_LF',"\n"); class helper_plugin_translate extends DokuWiki_Plugin { // vars used for caching / lazy initialization private $enabledLangs; private $langnames; private $langflags; private $page_language; private $curPageIsTranslatable; public function getMethods() { return array( array( 'name' => 'getPageLanguage', 'desc' => 'Returns a language code or null if failed to detect', 'return' => array('language' => 'string'), ), array( 'name' => 'languageExists', 'desc' => 'Checks if a language exists by checking the translations of dokuwiki itself', 'param' => array('language' => 'string'), 'return' => array('exists' => 'boolean'), ), array( 'name' => 'translationLookup', 'desc' => 'Returns the ID of a page (default the current page) translated in another language, or null if not existing.', 'param' => array('language' => 'string', 'id (optional)' => 'sting'), 'return' => array('translation_id' => 'string'), ), array( 'name' => 'suggestTranslationId', 'desc' => 'Returns an suggested ID of a translation of the current page, '. 'in the same or another namespace, according to the configuration', 'param' => array('title' => 'string', 'language' => 'sting', 'from_language' => 'string'), 'return' => array('translation_id' => 'string'), ), array( 'name' => 'getLanguageName', 'desc' => 'Returns the name of a language in the native language and English', 'param' => array('code' => 'string'), 'return' => array('name' => 'string'), ), // FIXME add the rest of them here..... ); } /** using guessing rules set in configuration */ public function getPageLanguage($id=null) { global $INFO, $ID, $conf; if (is_null($id)) $id = $ID; $meta = $id!==$ID ? p_get_metadata($id) : $INFO['meta']; if (!isset($this->page_language)) $this->page_language = array(); if (!isset($this->page_language[$id])) { // Detect the language of the page if (isset($meta['language'])) { $lang = $meta['language']; } if (!isset($lang) && $this->getConf('guess_lang_by_ns')) { // If the first level of namespace is a language code, use that list($ns1) = explode(':',$id,2); if ($this->languageExists($ns1)) $lang = $ns1; } if (!isset($lang) && $this->getConf('guess_lang_by_ui_lang')) { // Use the UI language $lang = $conf['lang']; } if (!isset($lang) && ($default = $this->getConf('default_language')) && $this->languageExists($default)) { // Use default language $lang = $default; } $this->page_language[$id] = $lang; } return $this->page_language[$id]; } /** checks if a language exists, i.e. is enabled. */ public function languageExists($lang) { return preg_match('/^\w{2,3}(?:-\w+)?$/', $lang) && is_dir(DOKU_INC . 'inc/lang/' . $lang); } public function getLanguageName($code) { $this->loadLangnames(); list ($key, $subkey) = explode('-',$code,2); // Locale style code, i.e. en-GB $name = isset($this->langnames[$key]) ? $this->langnames[$key] : $code; list ($name) = explode(',',$name,2); if ($subkey) $name .= '-'.strtoupper($subkey); return $name; } private function loadLangnames() { if (isset($this->langnames)) return; // A list where the first letter is capitalized only if // the local language has this convention if (@include(dirname(__FILE__).'/langinfo.php')) { foreach ($langinfo as $code => $info) { list ($local_name, $english_name) = $info; $this->langnames[$code] = $local_name; } } else { $this->langnames = array(); // failed to load } } /** Returns true if page is allowed to be translated */ public function isTranslatable($id=null) { global $ID; if (is_null($id)) $id = $ID; if ($id===$ID && isset($this->curPageIsTranslatable)) // cached return $this->curPageIsTranslatable; $ret = $this->checkIsTranslatable($id); if ($id===$ID) $this->curPageIsTranslatable = $ret; // cache return $ret; } /** helper used by isTranslatable */ private function checkIsTranslatable($id) { $str = trim($this->getConf('include_namespaces')); if ($str == '') return false; // nothing to include if ($str != '*') { $inc_nss = array_map('trim',explode(',',$str)); $any = false; foreach ($inc_nss as $ns) { if (self::isIdInNamespace($id,$ns)) { $any = true; break; } } if (!$any) return false; } $str = $this->getConf('exclude_namespaces'); if ($str != '') { $exc_nss = array_map('trim',explode(',',$str)); foreach ($exc_nss as $ns) if (self::isIdInNamespace($id,$ns)) return false; } $str = $this->getConf('exclude_pagenames'); if ($str != '') { $exc_pages = array_map('trim',explode(',',$str)); $p = noNS($id); foreach ($exc_pages as $exc) if ($p == $exc) return false; } // Finally check if the language can be detected $lang = $this->getPageLanguage($id); return !empty($lang); } private static function isIdInNamespace($id, $ns) { return substr($id, 0, strlen($ns) + 1) === "$ns:"; } /** Returns true if the current user is may translate the current page, false otherwise */ private function hasPermissionTranslate() { global $INFO; // Assume that if we have EDIT on the current page, we may also create translation pages. if ($INFO['perm'] >= AUTH_EDIT) return true; $grp = $this->getConf('translator_group'); return $grp && in_array($grp, $INFO['userinfo']['grps']); } /** Returns true if the current page is a translation, false otherwise. */ public function isTranslation($id=null) { global $ID; if (is_null($id)) $id = $ID; return $this->getOriginal($id) != $id; } /** Returns the id of the original page */ public function getOriginal($id=null) { global $INFO, $ID; if (is_null($id)) $id = $ID; $meta = $id!==$ID ? p_get_metadata($id) : $INFO['meta']; if (empty($meta['relation']['istranslationof'])) return $id; list ($orig) = array_keys($meta['relation']['istranslationof']); return $orig; } /** * Returns the ID of the current page translated in another language, or null if not existing. */ public function translationLookup($language, $id=null) { global $INFO, $ID; if (is_null($id)) $id = $ID; $id = $this->getOriginal($id); $orig_lang = $this->getPageLanguage($id); if ($language == $orig_lang) return $id; $meta = $id!==$ID ? p_get_metadata($id) : $INFO['meta']; if (!isset($meta['relation']['translations'])) return null; foreach ($meta['relation']['translations'] as $tid => $tlang) { if ($tlang == $language && page_exists($tid)) return $tid; } return null; } /** returns HTML for a link to a translation or a page where it can be created */ public function translationLink($language,$text='') { $langname = $this->getLanguageName($language); if ($text=='') { $text = $this->getConf('link_style') == 'langname' ? $langname : $language; } $current_lang = $this->getPageLanguage(); $original_id = $this->getOriginal(); $id = $this->translationLookup($language); if (isset($id)) { $url = wl($id); $class = 'wikilink1'; $more = ''; } else { $url = wl($original_id, array('do'=>'translate', 'to'=>$language)); $class = 'wikilink2'; $more = ' rel="nofollow"'; } return ''.hsc($text).''; } /** Returns HTML with translation links for all enabled languages */ public function translationLinksAll() { global $INFO, $ID; if (!$INFO['exists']) return; if (!$this->isTranslatable()) return; $orig = $this->getOriginal(); $origlang = $this->getPageLanguage($orig); // If no permission to translate, hide links to untranslated languages if ($this->hasPermissionTranslate()) { $langs = $this->getEnabledLanguages(); } else { // Show only languages with existing translations $langs = array_values($this->getTranslations($orig)); if (empty($langs)) return; // no translations exist } // Add link to the original language, if not present if (!in_array($origlang, $langs)) array_unshift($langs, $origlang); $out = '