Warning, file /sdk/pology/sieve/check_grammar.py was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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