Introduction to LanguageTool

LanguageTool 是一款开源的语言校正软件,用于文本的风格检测和语法纠错。她是一个规则系统,拥有超过2000条规则和一个功能强大的规则解析器。本文介绍了如何使用和拓展 LanguageTool。


Introduction

LanguageTool 是一个开源的语言校正软件,基于 Java 编写,在 GitHub 上有 2k+ star。

用于文本的风格检测和语法纠错。可以检测超过20种语言的风格和语法错误,More than a simple spell checker.

目前我司的 GEC 系统是一个规则与机器学习算法相结合的系统,包含了基于 CRF 的语法检测器 SyntaxCheck 和基于 rule 的 LanguageTool。未来我们会使用深度学习的模型,比如最近 MSRA 的一个语法纠错工作中用到的 seq2seq 模型。

LanguageTool 语法错误检测流程:

  1. 分句
  2. 分词
  3. 每个单词都分配一个词性标签 (e.g. cars = plural noun, talked = simple past verb)
  4. 带分析的文本将与内建 (built-in, 写在源代码中) 规则和从 grammar.xml 文件中加载的规则进行匹配

LanguageTool 是一个规则系统,所有错误的检测和修正都依赖于规则,截止到当前 (2018-08-13) 的版本 v4.3,共有 2117 条规则,其中 2105 条规则是由规则解析器从 grammar.xml 文件中加载;仅有 12 条规则是内置 (Built-in) 的,由 Java 代码实现。换句话说,99.4% 的规则是从文件中加载,并不需要我们写代码实现,LanguageTool 已经实现了功能强大的规则解析器。

优缺点

  • 缺点:所有 Pattern 都是人为制定,开发成本大,费时费力,无法穷尽所有语法错误
  • 优点:修正准确率高

要让 LanguageTool 变得更好,简单的说,就是写规则。无须编程,只要懂一些英语语法和 XML (Extensive Markup Language) 即可。

但是我不喜欢这个想法,因为写规则很无聊。我始终相信机器学习的算法是更有力量的,因为他能够自动学习 Pattern。

LanguageTool 的使命应该是我们的 GEC (Grammar Error Correction) 系统的一个 fail-safe。


Usage

Build

Scripted installation and building

To install or build using a script, simply type:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
curl -L https://git.io/vNqdP | sudo bash <options>
sudo bash install.sh <options>
Usage: install.sh <option> <package>
Options:
-h --help Show help
-b --build Builds packages from the bleeding edge development copy of LanguageTool
-c --command <command> Specifies post-installation command to run (default gui when screen is detected)
-q --quiet Shut up LanguageTool installer! Only tell me important stuff!
-t --text <file> Specifies what text to be spellchecked by LanguageTool command line (default spellcheck.txt)
-d --depth <value> Specifies the depth to clone when building LanguageTool yourself (default 1).
-p --package <package> Specifies package to install when building (default all)
-o --override <OS> Override automatic OS detection with <OS>
-a --accept Accept the oracle license at http://java.com/license. Only run this if you have seen the license and agree to its terms!
-r --remove <all/partial> Removes LanguageTool install. <all> uninstalls the dependencies that were auto-installed. (default partial)

Packages(only if -b is specified):
standalone Installs standalone package
wikipedia Installs Wikipedia package
office-extension Installs the LibreOffice/OpenOffice extension package

Commands:
GUI Runs GUI version of LanguageTool
commandline Runs command line version of LanguageTool
server Runs server version of LanguageTool

Build from source

Before start: you will need to clone from GitHub and install Java 8 and Apache Maven.

Warning: a complete clone requires downloading more than 360 MB and needs more than 500 MB on disk. This can be reduced if you only need the last few revisions of the master branch by creating a shallow clone:

1
git clone --depth 5 https://github.com/languagetool-org/languagetool.git

A shallow clone downloads less than 60 MB and needs less than 200 MB on disk.

In the root project folder, run:

1
mvn clean test

(sometimes you can skip Maven step for repeated builds)

1
./build.sh languagetool-standalone package -DskipTests

Test the result in languagetool-standalone/target/.

1
./build.sh languagetool-wikipedia package -DskipTests

Test the result in languagetool-wikipedia/target.

1
./build.sh languagetool-office-extension package -DskipTests

Test the result in languagetool-office-extension/target, rename the .zip to .oxt to install it in LibreOffice/OpenOffice.

Maven 的默认内存设置一般都很低,所以可能需要设置环境变量 MAVEN_OPTS 为 -Xmx512m

Now you can use the bleeding edge development copy of LanguageTool *.jar files, be aware that it might contain regressions.

API

Using LanguageTool via HTTP/HTTPS

LanguageTool 提供了 RESTful 服务,只要往他的 URL 发 POST 就可以使用 LanguageTool 的检测服务。

Remote Service Address: https://languagetool.org/api/v2/check

Python Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests

def post(essay):
url = "https://languagetool.org/api/v2/check"
payload = {
"text": essay,
"language": "en",
}
headers = {
'Content-Type': "application/x-www-form-urlencoded",
'Cache-Control': "no-cache",
}
result = requests.request("POST", url, data=payload, headers=headers).text
return result

Remote API 服务是受限制的,每分钟发送的次数和文本大小都受限,目前我们是起了一个本地的 LanguageTool service,来使用他的全部功能。

Start Local LanguageTool HTTP Server

为了启动 LanguageTool 的 HTTPServer,试了一下直接运行 build 好的 jar 包,发现不行,解决方法是指定主类运行。

1
2
cd path-to-languagetool/languagetool-standalone/target/LanguageTool-4.2-SNAPSHOT/LanguageTool-4.2-SNAPSHOT
java -cp languagetool-server.jar org.languagetool.server.HTTPServer

修改 IP Address 并开放公共访问

修改 languagetool/languagetool-server/src/main/java/org/languagetool/server/HTTPServerConfig.java 中的 DEFAULT_HOST = ‘10.7.13.73’,同时修改 publicAccess = true 开放公共访问,然后重新编译。

1
2
3
4
mvn clean
./build.sh languagetool-standalone package -DskipTests
cd path-to-languagetool/languagetool-standalone/target/LanguageTool-4.2-SNAPSHOT/LanguageTool-4.2-SNAPSHOT
java -cp languagetool-server.jar org.languagetool.server.HTTPServer

Using LanguageTool from a Java application

LanguageTool 需要 Java 8 或者以后的版本。

获取 LanguageTool 可以通过在官网下载 *.zip 安装包或者在 Maven pom.xml 中添加依赖:

1
2
3
4
5
<dependency>
<groupId>org.languagetool</groupId>
<artifactId>language-en</artifactId>
<version>4.2</version>
</dependency>

这会下载用于检测英语的相关依赖项。如需下载别的语言的 LanguageTool,修改 artifactId.

如果在 Java 代码中调用 LanguageTool?

只需要创建一个 JLanguageTool 对象并用她来检测文本即可。了解更多,请看 API文档

1
2
3
4
5
6
7
8
9
10
11
JLanguageTool langTool = new JLanguageTool(new BritishEnglish());
// comment in to use statistical ngram data:
// langTool.activateLanguageModelRules(new File("/data/google-ngram-data"));
List<RuleMatch> matches = langTool.check("A sentence with a error in the Hitchhiker's Guide tot he Galaxy");
for (RuleMatch match : matches) {
System.out.println("Potential error at characters " +
match.getFromPos() + "-" + match.getToPos() + ": " +
match.getMessage());
System.out.println("Suggested correction(s): " +
match.getSuggestedReplacements());
}

Embedding LanguageTool on a Web Page

想把 LanguageTool 集成到网页中,这也很 esay! 就像 LanguageTool 官网或者我们的 Essay Rater 中一样。

只需要2步,即可将 LanguageTool 集成到 Web 页面中:

  1. 在页面 (*.html) 的 \ 部分加入一些 Javascript 代码来调用 LT 的远程 HTTPS 服务
  2. 添加 HTML 代码来创建一个 textarea

Javascript includes and initialization code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<script type="text/javascript"
src="https://www.languagetool.org/js/jquery-1.7.0.min.js"></script>
<script type="text/javascript"
src="https://www.languagetool.org/online-check/tiny_mce/tiny_mce.js"></script>
<script type="text/javascript"
src="https://www.languagetool.org/online-check/tiny_mce/plugins/atd-tinymce/editor_plugin2.js"></script>
<script language="javascript" type="text/javascript">
tinyMCE.init({
mode : "textareas",
plugins : "AtD,paste",
paste_text_sticky : true,
setup : function(ed) {
ed.onInit.add(function(ed) {
ed.pasteAsPlainText = true;
});
},
/* translations: */
languagetool_i18n_no_errors : {
// "No errors were found.":
"de-DE": "Keine Fehler gefunden."
},
languagetool_i18n_explain : {
// "Explain..." - shown if there is an URL with a detailed description:
"de-DE": "Mehr Informationen..."
},
languagetool_i18n_ignore_once : {
// "Ignore this error":
"de-DE": "Hier ignorieren"
},
languagetool_i18n_ignore_all : {
// "Ignore this kind of error":
"de-DE": "Fehler dieses Typs ignorieren"
},
languagetool_i18n_rule_implementation : {
// "Rule implementation":
"de-DE": "Implementierung der Regel"
},
languagetool_i18n_current_lang :
function() { return document.checkform.lang.value; },
/* The URL of your LanguageTool server.
If you use your own server here and it's not running on the same domain
as the text form, make sure the server gets started with '--allow-origin ...'
and use 'https://your-server/v2/check' as URL: */
languagetool_rpc_url : "https://languagetool.org/api/v2/check",
/* edit this file to customize how LanguageTool shows errors: */
languagetool_css_url :
"https://www.languagetool.org/online-check/" +
"tiny_mce/plugins/atd-tinymce/css/content.css",
/* this stuff is a matter of preference: */
theme : "advanced",
theme_advanced_buttons1 : "",
theme_advanced_buttons2 : "",
theme_advanced_buttons3 : "",
theme_advanced_toolbar_location : "none",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
theme_advanced_path : false,
theme_advanced_resizing : true,
theme_advanced_resizing_use_cookie : false,
gecko_spellcheck : false
});
function doit() {
var langCode = document.checkform.lang.value;
tinyMCE.activeEditor.execCommand("mceWritingImprovementTool", langCode);
}
</script>

如果你使用的是本地 LT 服务,并且它和集成 LT 的网页不在同一个域名,会有跨域访问受阻的问题。确保用 —allow-origin ‘*’ 起你的本地服务。

HTML code for the textarea:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<form name="checkform" action="http://community.languagetool.org" method="post">
<p id="checktextpara">
<textarea id="checktext" name="text" style="width: 100%"
rows="6">Paste your own text here... or check check this text.</textarea>
</p>
<div>
<select name="lang" id="lang">
<option value="en-US">English</option>
<option value="de-DE">German</option>
<option value="it">Italian</option>
</select>
<input type="submit" name="_action_checkText"
value="Check Text" onClick="doit();return false;"> Powered by <a href="https://languagetool.org">languagetool.org</a>
<div id="feedbackErrorMessage" style="color: red;"></div>
</div>
</form>

Examples


Rules

Browse Rules

Rules Browser: https://community.languagetool.org/rule/list?lang=en

截止 2018-08-13,LanguageTool v4.3 共有 2117 条规则,分为 18 个 category,13 类从 grammar.xml 中加载,5 类是内建规则。

Category Quantity 是否在 grammar.xml 中
Capitalization 大小写错误 1 不在
Collocations 搭配错误 224
Commonly Confused Words 常见易混词 278
Creative Writing 创造性写作 2 不在
Grammar 语法错误 464
Miscellaneous 其他 5 不在
Misused Terms in EU publications (Gardner) 术语滥用 149
Nonstandard Phrases 不标准短语 27
Plain English 通俗英语 101
Possible Typo 错别字 554
Punctuation 标点缺失 3 不在
Punctuation Errors 标点错误 53
Redundant Phrases 冗余短语 164
Semantic 语义错误 32
Style 风格问题 34
Stylistic hints for creative writing 1 不在
Typography 格式错误 19
Wikipedia 维基百科 6

grammar.xml 中包含了13个种类:

  • category id: TYPOS, name: Possible Typo, type: misspelling
  • category id: GRAMMAR, name: Grammar, type: grammar
  • category id: COLLOCATIONS, name: Collocations, type: grammar
  • category id: PUNCTUATION, name: Punctuation Errors, type: typographical
  • category id: CONFUSED_WORDS, name: Commonly Confused Words, type: misspelling
  • category id: NONSTANDARD_PHRASES, name: Nonstandard Phrases, type: misspelling
  • category id: REDUNDANCY, name: Redundant Phrases, type: style
  • category id: STYLE, name: Style, type: style
  • category id: SEMANTICS, name: Semantic, type: inconsistency
  • category id: PLAIN_ENGLISH, name: Plain English, type: style
  • category id: WIKIPEDIA, name: Wikipedia, type: None
  • category id: TYPOGRAPHY, name: Typography, type: typographical
  • category id: MISUSED_TERMS_EU_PUBLICATIONS, name: Misused terms in EU publications (Gardner), type: style

Edit Rules

LanguageTool 目前的规则分为 18 类,其中 13 类是从 XML 规则文件中加载,由规则解析器去执行,通过修改规则文件可以新增或者删改规则;剩余的无法由 grammar.xml 中的 pattern 表示的规则是内建规则,写在 Java 源代码中,修改它们需要改源代码。

LanguageTool 提供了一个网页规则编辑器,可以自动生成 XML 形式的规则,非常方便。

增加新的 XML 规则

绝大多数规则都包含在 rules/xx/grammar.xml 中,xx 是语言代码比如 en 或者 de。

在源代码中,这个规则文件可以在 languagetool-language-modules/xx/src/main/resources/org/languagetool/ 目录下找到;独立的 GUI 程序中在 org/languagetool/ 目录下包含这个规则文件。

A rule is a pattern.

当 pattern 匹配到了文本,LT 会显示错误信息。pattern 可以处理 words 或者 pos tag。

举些规则中 token 元素的栗子:

1
<token>think</token>

匹配单词 think

1
<token>think</token> <token>about</token>

匹配短语 think about - 因为文本会被分词, 你需要把每个单词分别表示成一个 token.

这样是不行滴: <token>think about</token>

1
<token regexp="yes">think|say</token>

匹配正则表达式 think|say, i.e. 单词 think 或单词 say. 想了解更多关于正则表达式?请看这个教程

1
<token postag="VB" />

匹配一个基本形式的动词 (像 we have to walk 中的 walk).

查看 resource/en/tagset.txt 来获取所有的英语词性标记。

1
<token postag="V.*" postag_regexp="yes" />

匹配一个词性标记由 V 开头 (英语中,都是动词) 的单词。

注意,对于那些含有特殊字符的词性标记,比如 PRP$,需要将 $ 转义,因为它在正则中有特殊含义。

1
<token>cause</token> <token regexp="yes" negate="yes">and|to</token>

匹配单词 cause 后面跟的不是 and 或 to 的情况。

1
<token postag="SENT_START" /> <token>foobar</token>

匹配只出现在句子开头的单词 foobar。

句子结尾的 pos tag 是 SENT_END.

一个 pattern 中的 token 匹配默认是大小写不敏感的。可以通过设置 case_sensitive=“yes” 改变。

另外,单个 token 大小写敏感的匹配可以通过正则中的 (?-i) 来打开 (ex: <token regexp=“yes”>(?-i)Bill<token> 会匹配 Bill 而不是 bill)。

element token 只是规则中的一个元素,现在我们再来看看规则中还有哪些元素吧。

一条规则的基本元素

简单介绍一下基本的元素 (elements) 和他们的属性 (attributes):

  • element rule, attribute id: 用来定位这条规则的内部标识符. 必须是独一无二的 (unique).
  • element rule, attribute name: 描述这条规则的一段短文本.
  • element antipattern (optional, 可以出现多次): 规则的例外.
  • element pattern, sub element marker: 原始文本的哪个部分应该被标记成一个错误。如果所有 token 都是错误的一部分,可以忽略这个元素。
  • element token, attribute regexp: 加入设为 yes, 规则解析器会把给定的 token 当做正则表达式
  • element message: 当规则匹配到时显示给用户的文本。使用 sub-element suggestion 来给出给出一个可能的修正建议.
  • element url (optional): 解释规则更多细节的一个 URL. 假如包含字符 &, 需转义成 &.
  • element short (optional): 规则的一个简短的描述, 在 GUI 和 Libre/OpenOffice 的右键菜单中显示.
  • element example: 至少有2个例子,一个是正确的句子,一个是错误的句子。带有 attribute type=”incorrect” 的句子应该被该规则匹配。 错误的位置需由 sub-element marker 标记.

看两个完整的栗子:

这是一条奇怪的检测拼写错误的规则

1
2
3
4
5
6
7
8
9
<rule id="EXAMPLE_RULE" name="My example rule">
<pattern>
<token>foo</token>
<token>bar</token>
</pattern>
<message>Did you mean <suggestion>bicycle</suggestion>?</message>
<example type="incorrect">My <marker>foo bar</marker> is broken.</example>
<example type="correct">My bicycle is broken.</example>
</rule>

这是一条完整的规则,来检测 bed English, bat attitude, etc.

1
2
3
4
5
6
7
8
9
10
11
12
<rule id="BED_ENGLISH" name="Possible typo 'bed/bat(bad) English/...'">
<pattern>
<marker>
<token regexp="yes">bed|bat</token>
</marker>
<token regexp="yes">English|attitude</token>
</pattern>
<message>Did you mean <suggestion>bad</suggestion>?</message>
<url>http://some-server.org/the-bed-bad-error</url>
<example type="correct">Sorry for my <marker>bad</marker> English.</example>
<example correction="bad" type="incorrect">Sorry for my <marker>bed</marker> English.</example>
</rule>

打开 LanguageTool-xx/org/languagetool/rules/en/grammar.xml,将这些规则 XML 添加到相应的 \ 标签下,然后保存文件。重新编译工程,现在 LT 应该可以检测出 My foo bar is broken 和 bed English 中的错误了。

如果不想直接编辑 XML 文件,那么可以使用 在线规则编辑器 来自动生成 XML 规则。

LanguageTool 的规则解析器十分强大,支持多种 XML 元素和属性,支持多种正则表达式。当然,你的规则中如果出现了 LanguageTool 中没有预定义的 XML 元素,她也是不认识的。

想了解更多,请看这个文档

  • Regular Expressions
  • Inflection
  • Grouping rules
  • Whitespace
  • Categories
  • Turning rules off by default
  • Antipatterns
  • Min/Max
  • or, and
  • Skip
  • Variables
  • RuleFilter

增加新的 Java 规则

不在 grammar.xml 中的规则都是由 Java 代码编写 (一共才12条)。

如果你想用 Java 代码编写新的规则,拓展 LanguageTool 的 Rule 类并实现 match(AnalyzedSentence) 方法。如果你的规则不是在句子层面工作的,实现 TextLevelRule .

查看 DemoRule.java 中的小例子来了解如何开发新的规则。需要将你的 rule 类添加进 getRelevantRules() 方法来激活它。假如你使用的是 LanguageTool API,可以调用 JLanguageTool.addRule() .

汉化

编译完工程之后,程序会自动生成一个 grammar.xml,将汉化过的 (汉化主要指的是汉化规则中的 message 字段) 规则文件 grammar.xml 直接覆盖原来的 grammar.xml 即可汉化,无须重新编译。

1
2
cd path-to-languagetool/languagetool-standalone/target/LanguageTool-4.2-SNAPSHOT/LanguageTool-4.2-SNAPSHOT/org/languagetool/rules/en
vi grammar.xml

可能还需要汉化英文提示信息的资源文件。

直接修改 /languagetool/languagetool-core/src/main/resources/org/languagetool/MessagesBundle.properties 为中文的话会导致乱码,需要将中文转成 Unicode。

测试规则

在我们修改了规则文件 grammar.xml 之后,LanguageTool 应用 (languagetool.jar) 需要重启才会生效。

为了测试我们修改的规则是否符合预期,每次重启太麻烦了,可以使用 LanguageTool 内置的测试用例功能。

不同平台的测试方法 (语言码为 en)

  • Linux: 调用 sh testrules.sh en
  • Windows: 调用 testrules.bat en
  • Java: mvn clean test

这种测试方法会用规则的 examples 句子来测试你的所有规则:不正确的句子会被检测出来,正确的句子不会报错。如果不是这样,你会得到一个提示信息,告诉你规则或者例子不太对。

用测试脚本来测试比重启系统快太多,比较推荐。

Online Rule Editor

LanguageTool Rule Editor: https://community.languagetool.org/ruleEditor2/index?lang=en

介绍视频:https://www.youtube.com/watch?v=KnQCHo14gOU

Text Analyzer: https://community.languagetool.org/analysis/index?lang=en


References

资磁一下?