xref: /plugin/autotranslation/helper.php (revision bbe70520dcd3cf7c23d4a37b312b69b9c4aebd37)
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    var $LN          = array(); // hold native names
17    var $opts        = array(); // display options
18
19    /**
20     * Initialize
21     */
22    function helper_plugin_translation(){
23        global $conf;
24        require_once(DOKU_INC.'inc/pageutils.php');
25        require_once(DOKU_INC.'inc/utf8.php');
26
27        // load wanted translation into array
28        $this->trans = strtolower(str_replace(',',' ',$this->getConf('translations')));
29        $this->trans = array_unique(array_filter(explode(' ',$this->trans)));
30        sort($this->trans);
31
32        // load language names
33        $this->LN = confToHash(dirname(__FILE__).'/lang/langnames.txt');
34
35        // display options
36        $this->opts = $this->getConf('display');
37        $this->opts = explode(',',$this->opts);
38        $this->opts = array_map('trim',$this->opts);
39        $this->opts = array_fill_keys($this->opts, true);
40
41        // get default translation
42        if(!$conf['lang_before_translation']){
43            $dfl = $conf['lang'];
44        } else {
45            $dfl = $conf['lang_before_translation'];
46        }
47        if(in_array($dfl,$this->trans)){
48            $this->defaultlang = $dfl;
49        }else{
50            $this->defaultlang = '';
51            array_unshift($this->trans,'');
52        }
53
54        $this->tns = cleanID($this->getConf('translationns'));
55        if($this->tns) $this->tns .= ':';
56    }
57
58    /**
59     * Check if the given ID is a translation and return the language code.
60     */
61    function getLangPart($id){
62        list($lng) = $this->getTransParts($id);
63        return $lng;
64    }
65
66    /**
67     * Check if the given ID is a translation and return the language code and
68     * the id part.
69     */
70    function getTransParts($id){
71        $rx = '/^'.$this->tns.'('.join('|',$this->trans).'):(.*)/';
72        if(preg_match($rx,$id,$match)){
73            return array($match[1],$match[2]);
74        }
75        return array('',$id);
76    }
77
78    /**
79     * Returns the browser language if it matches with one of the configured
80     * languages
81     */
82    function getBrowserLang(){
83        $rx = '/(^|,|:|;|-)('.join('|',$this->trans).')($|,|:|;|-)/i';
84        if(preg_match($rx,$_SERVER['HTTP_ACCEPT_LANGUAGE'],$match)){
85            return strtolower($match[2]);
86        }
87        return false;
88    }
89
90    /**
91     * Returns the ID and name to the wanted translation, empty
92     * $lng is default lang
93     */
94    function buildTransID($lng,$idpart){
95        global $conf;
96        if($lng){
97            $link = ':'.$this->tns.$lng.':'.$idpart;
98            $name = $lng;
99        }else{
100            $link = ':'.$this->tns.$idpart;
101            $name = $this->realLC('');
102        }
103        return array($link,$name);
104    }
105
106    /**
107     * Returns the real language code, even when an empty one is given
108     * (eg. resolves th default language)
109     */
110    function realLC($lc){
111        global $conf;
112        if($lc){
113            return $lc;
114        }elseif(!$conf['lang_before_translation']){
115            return $conf['lang'];
116        } else {
117            return $conf['lang_before_translation'];
118        }
119    }
120
121    /**
122     * Check if current ID should be translated and any GUI
123     * should be shown
124     */
125    function istranslatable($id,$checkact=true){
126        global $ACT;
127
128        if($checkact && $ACT != 'show') return false;
129        if($this->tns && strpos($id,$this->tns) !== 0) return false;
130        $skiptrans = trim($this->getConf('skiptrans'));
131        if($skiptrans &&  preg_match('/'.$skiptrans.'/ui',':'.$id)) return false;
132        $meta = p_get_metadata($id);
133        if($meta['plugin']['translation']['notrans']) return false;
134
135        return true;
136    }
137
138    /**
139     * Return the (localized) about link
140     */
141    function showAbout() {
142        global $ID;
143        global $conf;
144        global $INFO;
145
146        $curlc = $this->getLangPart($ID);
147
148        $about = $this->getConf('about');
149        if($this->getConf('localabout')){
150            list($lc,$idpart) = $this->getTransParts($about);
151            list($about,$name) = $this->buildTransID($curlc,$idpart);
152            $about = cleanID($about);
153        }
154
155        $out = '';
156        $out .= '<sup>';
157        $out .= html_wikilink($about,'?');
158        $out .= '</sup>';
159
160        return $out;
161    }
162
163    /**
164     * Returns a list of (lc => link) for all existing translations of a page
165     *
166     * @param $id
167     * @return array
168     */
169    function getAvailableTranslations($id){
170        $result = array();
171
172        list($lc,$idpart) = $this->getTransParts($id);
173        $lang = $this->realLC($lc);
174
175        foreach($this->trans as $t){
176            if($t == $lc) continue; //skip self
177            list($link, $name) = $this->buildTransID($t, $idpart);
178            if(page_exists($link)){
179                $result[$name] = $link;
180            }
181        }
182
183        return $result;
184    }
185
186    /**
187     * Displays the available and configured translations. Needs to be placed in the template.
188     */
189    function showTranslations(){
190        global $ID;
191        global $conf;
192        global $INFO;
193
194        if(!$this->istranslatable($ID)) return;
195        $this->checkage();
196
197        list($lc,$idpart) = $this->getTransParts($ID);
198        $lang = $this->realLC($lc);
199
200
201        $out  = '<div class="plugin_translation">';
202
203        //show title and about
204        if (isset($this->opts['title'])){
205            $out .= '<span>'.$this->getLang('translations');
206            if ($this->getConf('about')) $out .= $this->showAbout();
207            $out .= ':</span> ';
208            if(isset($this->opts['twolines'])) $out .= '<br />';
209        }
210
211        // open wrapper
212        if($this->getConf('dropdown')){
213            // select needs its own styling
214            if($INFO['exists']){
215                $class = 'wikilink1';
216            }else{
217                $class = 'wikilink2';
218            }
219            if(isset($this->opts['flag'])){
220                $flag   = DOKU_BASE.'lib/plugins/translation/flags/'.hsc($lang).'.gif';
221            }
222            if($conf['userewrite']){
223                $action = wl();
224            }else{
225                $action = script();
226            }
227
228            $out .= '<form action="'.$action.'" id="translation__dropdown">';
229            if($flag) $out .= '<img src="'.$flag.'" alt="'.hsc($lang).'" height="11" class="'.$class.'" /> ';
230            $out .= '<select name="id" class="'.$class.'">';
231        }else{
232            $out .= '<ul>';
233        }
234
235        // insert items
236        foreach($this->trans as $t){
237            $out .= $this->getTransItem($t, $idpart);
238        }
239
240        // close wrapper
241        if($this->getConf('dropdown')){
242            $out .= '</select>';
243            $out .= '<input name="go" type="submit" value="&rarr;" />';
244            $out .= '</form>';
245        }else{
246            $out .= '</ul>';
247        }
248
249        // show about if not already shown
250        if (!isset($this->opts['title']) && $this->getConf('about')) {
251            $out .= '&nbsp';
252            $out .= $this->showAbout();
253        }
254
255        $out .= '</div>';
256
257        return $out;
258    }
259
260    /**
261     * Return the local name
262     *
263     * @param $lang
264     * @return string
265     */
266    function getLocalName($lang){
267        if ($this->LN[$lang]){
268            return $this->LN[$lang];
269        }
270        return $lang;
271    }
272
273    /**
274     * Create the link or option for a single translation
275     *
276     * @param $lc string      The language code
277     * @param $idpart string  The ID of the translated page
278     * @returns string        The item
279     */
280    function getTransItem($lc, $idpart) {
281        global $ID;
282        global $conf;
283
284        list($link,$lang) = $this->buildTransID($lc,$idpart);
285        $link = cleanID($link);
286
287        // class
288        if(page_exists($link,'',false)){
289            $class = 'wikilink1';
290        }else{
291            $class = 'wikilink2';
292        }
293
294        // local language name
295        $localname = $this->getLocalName($lang);
296
297        // current?
298        if($ID == $link){
299            $sel    = ' selected="selected"';
300            $class .= ' cur';
301        }else{
302            $sel    = '';
303        }
304
305        // flag
306        if(isset($this->opts['flag'])){
307            $flag   = DOKU_BASE.'lib/plugins/translation/flags/'.hsc($lang).'.gif';
308            $style  = ' style="background-image: url(\''.$flag.'\')"';
309            $class .= ' flag';
310        }
311
312        // what to display as name
313        if(isset($this->opts['name'])){
314            $display = hsc($localname);
315            if(isset($this->opts['langcode'])) $display .= ' ('.hsc($lang).')';
316        }elseif(isset($this->opts['langcode'])){
317            $display = hsc($lang);
318        }else{
319            $display = '&nbsp;';
320        }
321
322        // prepare output
323        $out = '';
324        if($this->getConf('dropdown')){
325            if($conf['useslash']) $link = str_replace(':', '/', $link);
326
327            $out .= '<option class="'.$class.'" title="'.hsc($localname).'" value="'.$link.'"'.$sel.$style.'>';
328            $out .= $display;
329            $out .= '</option>';
330        }else{
331            $out .= '<li><div class="li">';
332            $out .= '<a href="'.wl($link).'" class="'.$class.'" title="'.hsc($localname).'">';
333            if($flag) $out .= '<img src="'.$flag.'" alt="'.hsc($lang).'" height="11" />';
334            $out .= $display;
335            $out .= '</a>';
336            $out .= '</div></li>';
337        }
338
339        return $out;
340    }
341
342    /**
343     * Checks if the current page is a translation of a page
344     * in the default language. Displays a notice when it is
345     * older than the original page. Tries to lin to a diff
346     * with changes on the original since the translation
347     */
348    function checkage(){
349        global $ID;
350        global $INFO;
351        if(!$this->getConf('checkage')) return;
352        if(!$INFO['exists']) return;
353        $lng = $this->getLangPart($ID);
354        if($lng == $this->defaultlang) return;
355
356        $rx = '/^'.$this->tns.'(('.join('|',$this->trans).'):)?/';
357        $idpart = preg_replace($rx,'',$ID);
358
359        // compare modification times
360        list($orig,$name) = $this->buildTransID($this->defaultlang,$idpart);
361        $origfn = wikiFN($orig);
362        if($INFO['lastmod'] >= @filemtime($origfn) ) return;
363
364        // get revision from before translation
365        $orev = 0;
366        $revs = getRevisions($orig,0,100);
367        foreach($revs as $rev){
368            if($rev < $INFO['lastmod']){
369                $orev = $rev;
370                break;
371            }
372        }
373
374        // see if the found revision still exists
375        if($orev && !page_exists($orig,$orev)) $orev=0;
376
377        // build the message and display it
378        $orig = cleanID($orig);
379        $msg = sprintf($this->getLang('outdated'),wl($orig));
380        if($orev){
381            $msg .= sprintf(' '.$this->getLang('diff'),
382                    wl($orig,array('do'=>'diff','rev'=>$orev)));
383        }
384
385        echo '<div class="notify">'.$msg.'</div>';
386    }
387}
388