xref: /plugin/deeplautotranslate/action.php (revision 81931e50ee8dc27777ec37d0490b77b14f63aab6)
13832d0abSNetali<?php
23832d0abSNetali/**
33832d0abSNetali * Deepl Autotranslate Plugin
43832d0abSNetali *
53832d0abSNetali * @author     Jennifer Graul <me@netali.de>
63832d0abSNetali */
73832d0abSNetali
83832d0abSNetaliif(!defined('DOKU_INC')) die();
93832d0abSNetali
10*81931e50SNetaliuse \dokuwiki\HTTP\DokuHTTPClient;
11*81931e50SNetali
123832d0abSNetaliclass action_plugin_deeplautotranslate extends DokuWiki_Action_Plugin {
133832d0abSNetali
143832d0abSNetali    // manual mapping of ISO-languages to DeepL-languages to fix inconsistent naming
153832d0abSNetali    private $langs = [
163832d0abSNetali        'bg' => 'BG',
173832d0abSNetali        'cs' => 'CS',
183832d0abSNetali        'da' => 'DA',
193832d0abSNetali        'de' => 'DE',
203832d0abSNetali        'de-informal' => 'DE',
213832d0abSNetali        'el' => 'EL',
223832d0abSNetali        'en' => 'EN-GB',
233832d0abSNetali        'es' => 'ES',
243832d0abSNetali        'et' => 'ET',
253832d0abSNetali        'fi' => 'FI',
263832d0abSNetali        'fr' => 'FR',
273832d0abSNetali        'hu' => 'HU',
283832d0abSNetali        'hu-formal' => 'HU',
293832d0abSNetali        'it' => 'IT',
303832d0abSNetali        'ja' => 'JA',
313832d0abSNetali        'lt' => 'LT',
323832d0abSNetali        'lv' => 'LV',
333832d0abSNetali        'nl' => 'NL',
343832d0abSNetali        'pl' => 'PL',
353832d0abSNetali        'pt' => 'PT-PT',
363832d0abSNetali        'ro' => 'RO',
373832d0abSNetali        'ru' => 'RU',
383832d0abSNetali        'sk' => 'SK',
393832d0abSNetali        'sl' => 'SL',
403832d0abSNetali        'sv' => 'SV',
413832d0abSNetali        'zh' => 'ZH'
423832d0abSNetali    ];
433832d0abSNetali
443832d0abSNetali    /**
453832d0abSNetali     * Register its handlers with the DokuWiki's event controller
463832d0abSNetali     */
473832d0abSNetali    public function register(Doku_Event_Handler $controller) {
483832d0abSNetali        $controller->register_hook('ACTION_ACT_PREPROCESS','BEFORE', $this, 'autotrans_direct');
493832d0abSNetali        $controller->register_hook('COMMON_PAGETPL_LOAD','AFTER', $this, 'autotrans_editor');
503832d0abSNetali    }
513832d0abSNetali
523832d0abSNetali    public function autotrans_direct(Doku_Event $event, $param) {
533832d0abSNetali        if ($this->get_mode() != 'direct') return;
543832d0abSNetali        if ($event->data != 'show') return;
553832d0abSNetali
563832d0abSNetali        if (!$this->check_do_translation()) return;
573832d0abSNetali
583832d0abSNetali        global $ID;
593832d0abSNetali        global $INFO;
603832d0abSNetali
613832d0abSNetali        $org_page_text = $this->get_org_page_text();
623832d0abSNetali        $translated_text = $this->deepl_translate($org_page_text, $this->langs[$this->get_target_lang()]);
633832d0abSNetali
643832d0abSNetali        if ($translated_text === '') {
653832d0abSNetali            return;
663832d0abSNetali        }
673832d0abSNetali
683832d0abSNetali        saveWikiText($ID, $translated_text, 'Automatic translation');
693832d0abSNetali
703832d0abSNetali        $INFO = pageinfo();
713832d0abSNetali    }
723832d0abSNetali
733832d0abSNetali    public function autotrans_editor(Doku_Event $event, $param) {
743832d0abSNetali        if ($this->get_mode() != 'editor') return;
753832d0abSNetali
763832d0abSNetali        if (!$this->check_do_translation()) return;
773832d0abSNetali
783832d0abSNetali        $org_page_text = $this->get_org_page_text();
793832d0abSNetali
803832d0abSNetali        $event->data['tpl'] = $this->deepl_translate($org_page_text, $this->langs[$this->get_target_lang()]);
813832d0abSNetali    }
823832d0abSNetali
833832d0abSNetali    private function get_mode(): string {
843832d0abSNetali        global $ID;
853832d0abSNetali        if ($this->getConf('editor_regex')) {
863832d0abSNetali            if (preg_match('/' . $this->getConf('editor_regex') . '/', $ID) === 1) return 'editor';
873832d0abSNetali        }
883832d0abSNetali        if ($this->getConf('direct_regex')) {
893832d0abSNetali            if (preg_match('/' . $this->getConf('direct_regex') . '/', $ID) === 1) return 'direct';
903832d0abSNetali        }
913832d0abSNetali        return $this->getConf('mode');
923832d0abSNetali    }
933832d0abSNetali
943832d0abSNetali    private function get_target_lang(): string {
953832d0abSNetali        global $ID;
963832d0abSNetali        $split_id = explode(':', $ID);
973832d0abSNetali        return array_shift($split_id);
983832d0abSNetali    }
993832d0abSNetali
1003832d0abSNetali    private function get_org_page_text(): string {
1013832d0abSNetali        global $ID;
1023832d0abSNetali
1033832d0abSNetali        $split_id = explode(':', $ID);
1043832d0abSNetali        array_shift($split_id);
1053832d0abSNetali        $org_id = implode(':', $split_id);
1063832d0abSNetali
1073832d0abSNetali        return rawWiki($org_id);
1083832d0abSNetali    }
1093832d0abSNetali
1103832d0abSNetali    private function check_do_translation(): bool {
1113832d0abSNetali        global $INFO;
1123832d0abSNetali        // only translate if the current page does not exist
1133832d0abSNetali        if ($INFO['exists']) return false;
1143832d0abSNetali
1153832d0abSNetali        global $ID;
1163832d0abSNetali
1173832d0abSNetali        // skip blacklisted namespaces and pages
1183832d0abSNetali        if ($this->getConf('blacklist_regex')) {
1193832d0abSNetali            if (preg_match('/' . $this->getConf('blacklist_regex') . '/', $ID) === 1) return false;
1203832d0abSNetali        }
1213832d0abSNetali
1223832d0abSNetali        $split_id = explode(':', $ID);
1233832d0abSNetali        $lang_ns = array_shift($split_id);
1243832d0abSNetali        // only translate if the current page is in a language namespace
1253832d0abSNetali        if (!array_key_exists($lang_ns, $this->langs)) return false;
1263832d0abSNetali
1273832d0abSNetali        $org_id = implode(':', $split_id);
1283832d0abSNetali        // check if the original page exists
1293832d0abSNetali        if (!page_exists($org_id)) return false;
1303832d0abSNetali
1313832d0abSNetali        return true;
1323832d0abSNetali    }
1333832d0abSNetali
1343832d0abSNetali    private function deepl_translate($text, $target_lang): string {
1353832d0abSNetali        if (!$this->getConf('api_key')) return '';
1363832d0abSNetali
1373832d0abSNetali        $text = $this->insert_ignore_tags($text);
1383832d0abSNetali
1393832d0abSNetali        $data = [
1403832d0abSNetali            'auth_key' => $this->getConf('api_key'),
1413832d0abSNetali            'target_lang' => $target_lang,
1423832d0abSNetali            'tag_handling' => 'xml',
1433832d0abSNetali            'ignore_tags' => 'ignore',
1443832d0abSNetali            'text' => $text
1453832d0abSNetali        ];
1463832d0abSNetali
1473832d0abSNetali        if ($this->getConf('api') == 'free') {
148*81931e50SNetali            $url = 'https://api-free.deepl.com/v2/translate';
1493832d0abSNetali        } else {
150*81931e50SNetali            $url = 'https://api.deepl.com/v2/translate';
1513832d0abSNetali        }
1523832d0abSNetali
153*81931e50SNetali        $http = new DokuHTTPClient();
154*81931e50SNetali        $raw_response = $http->post($url, $data);
1553832d0abSNetali
1563832d0abSNetali        // if any error occurred return an empty string
157*81931e50SNetali        if ($http->status >= 400) return '';
1583832d0abSNetali
1593832d0abSNetali        $json_response = json_decode($raw_response, true);
1603832d0abSNetali        $translated_text = $json_response['translations'][0]['text'];
1613832d0abSNetali
1623832d0abSNetali        $translated_text = $this->remove_ignore_tags($translated_text);
1633832d0abSNetali
1643832d0abSNetali        return $translated_text;
1653832d0abSNetali    }
1663832d0abSNetali
1673832d0abSNetali    private function insert_ignore_tags($text): string {
1683832d0abSNetali        $text = str_replace('[[', '<ignore>[[', $text);
1693832d0abSNetali        $text = str_replace('{{', '<ignore>{{', $text);
1703832d0abSNetali        $text = str_replace(']]', ']]</ignore>', $text);
1713832d0abSNetali        $text = str_replace('}}', '}}</ignore>', $text);
1723832d0abSNetali
1733832d0abSNetali        $ignored_expressions = explode(':', $this->getConf('ignored_expressions'));
1743832d0abSNetali
1753832d0abSNetali        foreach ($ignored_expressions as $expression) {
1763832d0abSNetali            $text = str_replace($expression, '<ignore>' . $expression . '</ignore>', $text);
1773832d0abSNetali        }
1783832d0abSNetali
1793832d0abSNetali        return $text;
1803832d0abSNetali    }
1813832d0abSNetali
1823832d0abSNetali    private function remove_ignore_tags($text): string {
1833832d0abSNetali        $text = str_replace('<ignore>[[', '[[', $text);
1843832d0abSNetali        $text = str_replace('<ignore>{{', '{{', $text);
1853832d0abSNetali        $text = str_replace(']]</ignore>', ']]', $text);
1863832d0abSNetali        $text = str_replace('}}</ignore>', '}}', $text);
1873832d0abSNetali
1883832d0abSNetali        $ignored_expressions = explode(':', $this->getConf('ignored_expressions'));
1893832d0abSNetali
1903832d0abSNetali        foreach ($ignored_expressions as $expression) {
1913832d0abSNetali            $text = str_replace('<ignore>' . $expression . '</ignore>', $expression, $text);
1923832d0abSNetali        }
1933832d0abSNetali
1943832d0abSNetali        return $text;
1953832d0abSNetali    }
1963832d0abSNetali}
1973832d0abSNetali
198