File indexing completed on 2024-12-08 05:09:21
0001 # -*- coding: UTF-8 -*- 0002 0003 """ 0004 Check language of translation using LanguageTool. 0005 0006 Documented in C{doc/user/sieving.docbook}. 0007 0008 @author: Sébastien Renard <sebastien.renard@digitalfox.org> 0009 @license: GPLv3 0010 """ 0011 0012 import json 0013 from http.client import HTTPConnection 0014 import socket 0015 import sys 0016 from urllib.parse import urlencode 0017 from xml.dom.minidom import parseString 0018 0019 from pology import _, n_ 0020 from pology.colors import cjoin 0021 from pology.msgreport import report_msg_to_lokalize, warning_on_msg 0022 from pology.report import report, warning 0023 from pology.sieve import SieveError, SieveCatalogError 0024 from pology.sieve import add_param_lang, add_param_accel, add_param_markup 0025 from pology.sieve import add_param_filter 0026 from pology.getfunc import get_hook_ireq 0027 from pology.sieve import add_param_poeditors 0028 0029 0030 _REQUEST="/v2/check?language=%s&disabledRules=%s&%s" 0031 0032 def setup_sieve (p): 0033 0034 p.set_desc(_("@info sieve discription", 0035 "Check language of translation using LanguageTool." 0036 "\n\n" 0037 "LanguageTool (http://www.languagetool.org) is an open source " 0038 "language checker, which may be used as a standalone application, " 0039 "or in server-client mode. " 0040 "This sieve runs in client-server mode, so make sure Language Tool " 0041 "is running before this sieve is run." 0042 )) 0043 0044 add_param_lang(p) 0045 add_param_accel(p) 0046 add_param_markup(p) 0047 add_param_filter(p, 0048 intro=_("@info sieve parameter discription", 0049 "The F1A or F3A/C hook through which to filter the translation " 0050 "before passing it to grammar checking." 0051 )) 0052 p.add_param("host", str, defval="localhost", 0053 metavar=_("@info sieve parameter value placeholder", "NAME"), 0054 desc=_("@info sieve parameter discription", 0055 "Name of the host where the server is running." 0056 )) 0057 p.add_param("port", str, defval="8081", 0058 metavar=_("@info sieve parameter value placeholder", "NUMBER"), 0059 desc=_("@info sieve parameter discription", 0060 "TCP port on the host which server uses to listen for queries." 0061 )) 0062 0063 add_param_poeditors(p) 0064 0065 0066 class Sieve (object): 0067 0068 def __init__ (self, params): 0069 0070 self.nmatch = 0 # Number of match for finalize 0071 self.connection=None # Connection to LanguageTool server 0072 0073 self.setLang=params.lang 0074 self.setAccel=params.accel 0075 self.setMarkup=params.markup 0076 self.lokalize = params.lokalize 0077 0078 # LanguageTool server parameters. 0079 host=params.host 0080 port=params.port 0081 #TODO: autodetect tcp port by reading LanguageTool config file if host is localhost 0082 0083 # As LT server does not seem to read disabled rules from his config file, we manage exception here 0084 #TODO: investigate deeper this problem and make a proper bug report to LT devs. 0085 self.disabledRules=["HUNSPELL_RULE","UPPERCASE_SENTENCE_START","COMMA_PARENTHESIS_WHITESPACE"] 0086 0087 # Create connection to the LanguageTool server 0088 self.connection=HTTPConnection(host, port) 0089 0090 self.pfilters = [[get_hook_ireq(x, abort=True), x] 0091 for x in (params.filter or [])] 0092 0093 0094 def process_header (self, hdr, cat): 0095 0096 self.lang=(self.setLang or cat.language()) 0097 if not self.lang: 0098 raise SieveCatalogError( 0099 _("@info", 0100 "Cannot determine language for catalog '%(file)s'.", 0101 file=cat.filename)) 0102 0103 # Force explicitly given accelerators and markup. 0104 if self.setAccel is not None: 0105 cat.set_accelerator(self.setAccel) 0106 if self.setMarkup is not None: 0107 cat.set_markup(self.setMarkup) 0108 0109 0110 def process (self, msg, cat): 0111 0112 if msg.obsolete: 0113 return 0114 0115 try: 0116 for msgstr in msg.msgstr: 0117 0118 # Apply precheck filters. 0119 for pfilter, pfname in self.pfilters: 0120 try: # try as type F1A hook 0121 msgstr = pfilter(msgstr) 0122 except TypeError: 0123 try: # try as type F3* hook 0124 msgstr = pfilter(msgstr, msg, cat) 0125 except TypeError: 0126 raise SieveError( 0127 _("@info", 0128 "Cannot execute filter '%(filt)s'.", 0129 filt=pfname)) 0130 0131 self.connection.request( 0132 "GET", 0133 _REQUEST % ( 0134 self.lang, 0135 ",".join(self.disabledRules), 0136 urlencode({"text":msgstr.encode("UTF-8")}), 0137 ) 0138 ) 0139 response=self.connection.getresponse() 0140 if not response: 0141 continue 0142 matches = json.loads(response.read())["matches"] 0143 if not matches: 0144 continue 0145 for match in matches: 0146 self.nmatch+=1 0147 report("-"*(len(msgstr)+8)) 0148 report(_("@info", 0149 "<bold>%(file)s:%(line)d(#%(entry)d)</bold>", 0150 file=cat.filename, line=msg.refline, entry=msg.refentry)) 0151 #TODO: create a report function in the right place 0152 #TODO: color in red part of context that make the mistake 0153 report(_("@info", 0154 "<bold>Context:</bold> %(snippet)s", 0155 snippet=match["context"]["text"])) 0156 report(_("@info", 0157 "(%(rule)s) <bold><red>==></red></bold> %(note)s", 0158 rule=match["rule"]["id"], 0159 note=match["message"])) 0160 report("") 0161 if self.lokalize: 0162 repls = [_("@label", "Grammar errors:")] 0163 repls.append(_( 0164 "@info", 0165 "<bold>%(file)s:%(line)d(#%(entry)d)</bold>", 0166 file=cat.filename, 0167 line=msg.refline, 0168 entry=msg.refentry 0169 )) 0170 repls.append(_( 0171 "@info", 0172 "(%(rule)s) <bold><red>==></red></bold> %(note)s", 0173 rule=match["rule"]["id"], 0174 note=match["message"] 0175 )) 0176 report_msg_to_lokalize(msg, cat, cjoin(repls, "\n")) 0177 except socket.error: 0178 raise SieveError(_("@info", 0179 "Cannot connect to LanguageTool server. " 0180 "Did you start it?")) 0181 0182 0183 def finalize (self): 0184 if self.nmatch: 0185 msg = n_("@info:progress", 0186 "Detected %(num)d problem in grammar and style.", 0187 "Detected %(num)d problems in grammar and style.", 0188 num=self.nmatch) 0189 report("===== " + msg) 0190