1*3832d0abSNetali<?php 2*3832d0abSNetali/** 3*3832d0abSNetali * Deepl Autotranslate Plugin 4*3832d0abSNetali * 5*3832d0abSNetali * @author Jennifer Graul <me@netali.de> 6*3832d0abSNetali */ 7*3832d0abSNetali 8*3832d0abSNetaliif(!defined('DOKU_INC')) die(); 9*3832d0abSNetali 10*3832d0abSNetaliclass action_plugin_deeplautotranslate extends DokuWiki_Action_Plugin { 11*3832d0abSNetali 12*3832d0abSNetali // manual mapping of ISO-languages to DeepL-languages to fix inconsistent naming 13*3832d0abSNetali private $langs = [ 14*3832d0abSNetali 'bg' => 'BG', 15*3832d0abSNetali 'cs' => 'CS', 16*3832d0abSNetali 'da' => 'DA', 17*3832d0abSNetali 'de' => 'DE', 18*3832d0abSNetali 'de-informal' => 'DE', 19*3832d0abSNetali 'el' => 'EL', 20*3832d0abSNetali 'en' => 'EN-GB', 21*3832d0abSNetali 'es' => 'ES', 22*3832d0abSNetali 'et' => 'ET', 23*3832d0abSNetali 'fi' => 'FI', 24*3832d0abSNetali 'fr' => 'FR', 25*3832d0abSNetali 'hu' => 'HU', 26*3832d0abSNetali 'hu-formal' => 'HU', 27*3832d0abSNetali 'it' => 'IT', 28*3832d0abSNetali 'ja' => 'JA', 29*3832d0abSNetali 'lt' => 'LT', 30*3832d0abSNetali 'lv' => 'LV', 31*3832d0abSNetali 'nl' => 'NL', 32*3832d0abSNetali 'pl' => 'PL', 33*3832d0abSNetali 'pt' => 'PT-PT', 34*3832d0abSNetali 'ro' => 'RO', 35*3832d0abSNetali 'ru' => 'RU', 36*3832d0abSNetali 'sk' => 'SK', 37*3832d0abSNetali 'sl' => 'SL', 38*3832d0abSNetali 'sv' => 'SV', 39*3832d0abSNetali 'zh' => 'ZH' 40*3832d0abSNetali ]; 41*3832d0abSNetali 42*3832d0abSNetali /** 43*3832d0abSNetali * Register its handlers with the DokuWiki's event controller 44*3832d0abSNetali */ 45*3832d0abSNetali public function register(Doku_Event_Handler $controller) { 46*3832d0abSNetali $controller->register_hook('ACTION_ACT_PREPROCESS','BEFORE', $this, 'autotrans_direct'); 47*3832d0abSNetali $controller->register_hook('COMMON_PAGETPL_LOAD','AFTER', $this, 'autotrans_editor'); 48*3832d0abSNetali } 49*3832d0abSNetali 50*3832d0abSNetali public function autotrans_direct(Doku_Event $event, $param) { 51*3832d0abSNetali if ($this->get_mode() != 'direct') return; 52*3832d0abSNetali if ($event->data != 'show') return; 53*3832d0abSNetali 54*3832d0abSNetali if (!$this->check_do_translation()) return; 55*3832d0abSNetali 56*3832d0abSNetali global $ID; 57*3832d0abSNetali global $INFO; 58*3832d0abSNetali 59*3832d0abSNetali $org_page_text = $this->get_org_page_text(); 60*3832d0abSNetali $translated_text = $this->deepl_translate($org_page_text, $this->langs[$this->get_target_lang()]); 61*3832d0abSNetali 62*3832d0abSNetali if ($translated_text === '') { 63*3832d0abSNetali return; 64*3832d0abSNetali } 65*3832d0abSNetali 66*3832d0abSNetali saveWikiText($ID, $translated_text, 'Automatic translation'); 67*3832d0abSNetali 68*3832d0abSNetali $INFO = pageinfo(); 69*3832d0abSNetali } 70*3832d0abSNetali 71*3832d0abSNetali public function autotrans_editor(Doku_Event $event, $param) { 72*3832d0abSNetali if ($this->get_mode() != 'editor') return; 73*3832d0abSNetali 74*3832d0abSNetali if (!$this->check_do_translation()) return; 75*3832d0abSNetali 76*3832d0abSNetali $org_page_text = $this->get_org_page_text(); 77*3832d0abSNetali 78*3832d0abSNetali $event->data['tpl'] = $this->deepl_translate($org_page_text, $this->langs[$this->get_target_lang()]); 79*3832d0abSNetali } 80*3832d0abSNetali 81*3832d0abSNetali private function get_mode(): string { 82*3832d0abSNetali global $ID; 83*3832d0abSNetali if ($this->getConf('editor_regex')) { 84*3832d0abSNetali if (preg_match('/' . $this->getConf('editor_regex') . '/', $ID) === 1) return 'editor'; 85*3832d0abSNetali } 86*3832d0abSNetali if ($this->getConf('direct_regex')) { 87*3832d0abSNetali if (preg_match('/' . $this->getConf('direct_regex') . '/', $ID) === 1) return 'direct'; 88*3832d0abSNetali } 89*3832d0abSNetali return $this->getConf('mode'); 90*3832d0abSNetali } 91*3832d0abSNetali 92*3832d0abSNetali private function get_target_lang(): string { 93*3832d0abSNetali global $ID; 94*3832d0abSNetali $split_id = explode(':', $ID); 95*3832d0abSNetali return array_shift($split_id); 96*3832d0abSNetali } 97*3832d0abSNetali 98*3832d0abSNetali private function get_org_page_text(): string { 99*3832d0abSNetali global $ID; 100*3832d0abSNetali 101*3832d0abSNetali $split_id = explode(':', $ID); 102*3832d0abSNetali array_shift($split_id); 103*3832d0abSNetali $org_id = implode(':', $split_id); 104*3832d0abSNetali 105*3832d0abSNetali return rawWiki($org_id); 106*3832d0abSNetali } 107*3832d0abSNetali 108*3832d0abSNetali private function check_do_translation(): bool { 109*3832d0abSNetali global $INFO; 110*3832d0abSNetali // only translate if the current page does not exist 111*3832d0abSNetali if ($INFO['exists']) return false; 112*3832d0abSNetali 113*3832d0abSNetali global $ID; 114*3832d0abSNetali 115*3832d0abSNetali // skip blacklisted namespaces and pages 116*3832d0abSNetali if ($this->getConf('blacklist_regex')) { 117*3832d0abSNetali if (preg_match('/' . $this->getConf('blacklist_regex') . '/', $ID) === 1) return false; 118*3832d0abSNetali } 119*3832d0abSNetali 120*3832d0abSNetali $split_id = explode(':', $ID); 121*3832d0abSNetali $lang_ns = array_shift($split_id); 122*3832d0abSNetali // only translate if the current page is in a language namespace 123*3832d0abSNetali if (!array_key_exists($lang_ns, $this->langs)) return false; 124*3832d0abSNetali 125*3832d0abSNetali $org_id = implode(':', $split_id); 126*3832d0abSNetali // check if the original page exists 127*3832d0abSNetali if (!page_exists($org_id)) return false; 128*3832d0abSNetali 129*3832d0abSNetali return true; 130*3832d0abSNetali } 131*3832d0abSNetali 132*3832d0abSNetali private function deepl_translate($text, $target_lang): string { 133*3832d0abSNetali if (!$this->getConf('api_key')) return ''; 134*3832d0abSNetali $curl = curl_init(); 135*3832d0abSNetali 136*3832d0abSNetali $text = $this->insert_ignore_tags($text); 137*3832d0abSNetali 138*3832d0abSNetali $data = [ 139*3832d0abSNetali 'auth_key' => $this->getConf('api_key'), 140*3832d0abSNetali 'target_lang' => $target_lang, 141*3832d0abSNetali 'tag_handling' => 'xml', 142*3832d0abSNetali 'ignore_tags' => 'ignore', 143*3832d0abSNetali 'text' => $text 144*3832d0abSNetali ]; 145*3832d0abSNetali 146*3832d0abSNetali if ($this->getConf('api') == 'free') { 147*3832d0abSNetali curl_setopt($curl, CURLOPT_URL, 'https://api-free.deepl.com/v2/translate'); 148*3832d0abSNetali } else { 149*3832d0abSNetali curl_setopt($curl, CURLOPT_URL, 'https://api.deepl.com/v2/translate'); 150*3832d0abSNetali } 151*3832d0abSNetali 152*3832d0abSNetali curl_setopt($curl,CURLOPT_POST, 1); 153*3832d0abSNetali curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 154*3832d0abSNetali curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 155*3832d0abSNetali 156*3832d0abSNetali $raw_response = curl_exec($curl); 157*3832d0abSNetali 158*3832d0abSNetali // if any error occurred return an empty string 159*3832d0abSNetali if (curl_getinfo($curl, CURLINFO_RESPONSE_CODE) >= 400) { 160*3832d0abSNetali curl_close($curl); 161*3832d0abSNetali return ''; 162*3832d0abSNetali } 163*3832d0abSNetali 164*3832d0abSNetali $json_response = json_decode($raw_response, true); 165*3832d0abSNetali $translated_text = $json_response['translations'][0]['text']; 166*3832d0abSNetali 167*3832d0abSNetali $translated_text = $this->remove_ignore_tags($translated_text); 168*3832d0abSNetali 169*3832d0abSNetali return $translated_text; 170*3832d0abSNetali } 171*3832d0abSNetali 172*3832d0abSNetali private function insert_ignore_tags($text): string { 173*3832d0abSNetali $text = str_replace('[[', '<ignore>[[', $text); 174*3832d0abSNetali $text = str_replace('{{', '<ignore>{{', $text); 175*3832d0abSNetali $text = str_replace(']]', ']]</ignore>', $text); 176*3832d0abSNetali $text = str_replace('}}', '}}</ignore>', $text); 177*3832d0abSNetali 178*3832d0abSNetali $ignored_expressions = explode(':', $this->getConf('ignored_expressions')); 179*3832d0abSNetali 180*3832d0abSNetali foreach ($ignored_expressions as $expression) { 181*3832d0abSNetali $text = str_replace($expression, '<ignore>' . $expression . '</ignore>', $text); 182*3832d0abSNetali } 183*3832d0abSNetali 184*3832d0abSNetali return $text; 185*3832d0abSNetali } 186*3832d0abSNetali 187*3832d0abSNetali private function remove_ignore_tags($text): string { 188*3832d0abSNetali $text = str_replace('<ignore>[[', '[[', $text); 189*3832d0abSNetali $text = str_replace('<ignore>{{', '{{', $text); 190*3832d0abSNetali $text = str_replace(']]</ignore>', ']]', $text); 191*3832d0abSNetali $text = str_replace('}}</ignore>', '}}', $text); 192*3832d0abSNetali 193*3832d0abSNetali $ignored_expressions = explode(':', $this->getConf('ignored_expressions')); 194*3832d0abSNetali 195*3832d0abSNetali foreach ($ignored_expressions as $expression) { 196*3832d0abSNetali $text = str_replace('<ignore>' . $expression . '</ignore>', $expression, $text); 197*3832d0abSNetali } 198*3832d0abSNetali 199*3832d0abSNetali return $text; 200*3832d0abSNetali } 201*3832d0abSNetali} 202*3832d0abSNetali 203