@valueerror/ltjs-lang-fr 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +232 -0
- package/NOTICE +9 -0
- package/README.md +25 -2
- package/index.js +24 -0
- package/licenses/LGPL-2.1.txt +504 -0
- package/licenses/french-pos-dict.pom.xml +115 -0
- package/licenses/resource_fr_README_lexique.txt +30 -0
- package/licenses/resource_fr_hunspell_README_fr.txt +73 -0
- package/package.json +60 -4
- package/resource-root.js +4 -0
- package/resources/META-INF/org/languagetool/language-module.properties +1 -0
- package/resources/org/languagetool/resource/fr/README_lexique.txt +30 -0
- package/resources/org/languagetool/resource/fr/added.txt +477 -0
- package/resources/org/languagetool/resource/fr/added_custom.txt +4 -0
- package/resources/org/languagetool/resource/fr/compounds.txt +1443 -0
- package/resources/org/languagetool/resource/fr/confusion_sets.txt +76 -0
- package/resources/org/languagetool/resource/fr/disambiguation.xml +11391 -0
- package/resources/org/languagetool/resource/fr/fr.sor +162 -0
- package/resources/org/languagetool/resource/fr/french.dict +0 -0
- package/resources/org/languagetool/resource/fr/french.info +20 -0
- package/resources/org/languagetool/resource/fr/french_synth.dict +0 -0
- package/resources/org/languagetool/resource/fr/french_synth.info +4 -0
- package/resources/org/languagetool/resource/fr/french_tags.txt +268 -0
- package/resources/org/languagetool/resource/fr/hunspell/README_fr.txt +73 -0
- package/resources/org/languagetool/resource/fr/hunspell/ignore.txt +1551 -0
- package/resources/org/languagetool/resource/fr/hunspell/prohibit.txt +3 -0
- package/resources/org/languagetool/resource/fr/hunspell/spelling.txt +34735 -0
- package/resources/org/languagetool/resource/fr/hunspell/spelling_custom.txt +1 -0
- package/resources/org/languagetool/resource/fr/hyphenated_words.txt +12864 -0
- package/resources/org/languagetool/resource/fr/multiwords.txt +4886 -0
- package/resources/org/languagetool/resource/fr/removed.txt +5 -0
- package/resources/org/languagetool/resource/fr/removed_custom.txt +4 -0
- package/resources/org/languagetool/rules/fr/grammar.xml +151570 -0
- package/resources/org/languagetool/rules/fr/remote-rule-filters.xml +820 -0
- package/resources/org/languagetool/rules/fr/replace.txt +39 -0
- package/resources/org/languagetool/rules/fr/replace_custom.txt +7 -0
- package/resources/org/languagetool/rules/fr/style.xml +22790 -0
- package/resources/org/languagetool/rules/fr/synonyms.txt +203 -0
- package/src/lang/fr/language/BelgianFrench.js +38 -0
- package/src/lang/fr/language/CanadianFrench.js +38 -0
- package/src/lang/fr/language/French.js +478 -0
- package/src/lang/fr/language/SwissFrench.js +38 -0
- package/src/lang/fr/language/id2prioFrench.js +102 -0
- package/src/lang/fr/rules/AdvancedSynthesizerFilter.js +12 -0
- package/src/lang/fr/rules/CompoundRule.js +47 -0
- package/src/lang/fr/rules/DMYDateCheckFilter.js +31 -0
- package/src/lang/fr/rules/DateCheckFilter.js +35 -0
- package/src/lang/fr/rules/DateFilterHelper.js +55 -0
- package/src/lang/fr/rules/FindSuggestionsFilter.js +80 -0
- package/src/lang/fr/rules/FrenchLazy.js +9 -0
- package/src/lang/fr/rules/FrenchMultitokenSpeller.js +15 -0
- package/src/lang/fr/rules/FrenchNumberInWordFilter.js +29 -0
- package/src/lang/fr/rules/FrenchPartialPosTagFilter.js +29 -0
- package/src/lang/fr/rules/FrenchRepeatedWordsRule.js +76 -0
- package/src/lang/fr/rules/FrenchSuppressMisspelledSuggestionsFilter.js +12 -0
- package/src/lang/fr/rules/InterrogativeVerbFilter.js +159 -0
- package/src/lang/fr/rules/MakeContractionsFilter.js +32 -0
- package/src/lang/fr/rules/MorfologikFrenchSpellerRule.js +262 -0
- package/src/lang/fr/rules/NewYearDateFilter.js +30 -0
- package/src/lang/fr/rules/PostponedAdjectiveConcordanceFilter.js +512 -0
- package/src/lang/fr/rules/QuestionWhitespaceRule.js +171 -0
- package/src/lang/fr/rules/QuestionWhitespaceStrictRule.js +38 -0
- package/src/lang/fr/rules/SimpleReplaceRule.js +63 -0
- package/src/lang/fr/rules/SuggestionsFilter.js +29 -0
- package/src/lang/fr/rules/WordWithDeterminerFilter.js +168 -0
- package/src/lang/fr/synthesis/FrenchSynthesizer.js +36 -0
- package/src/lang/fr/tagging/FrenchHybridDisambiguator.js +28 -0
- package/src/lang/fr/tagging/FrenchTagger.js +176 -0
- package/src/lang/fr/tokenizers/FrenchWordTokenizer.js +164 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// org.languagetool.rules.fr.QuestionWhitespaceStrictRule
|
|
2
|
+
import { QuestionWhitespaceRule } from './QuestionWhitespaceRule.js';
|
|
3
|
+
import { Tag } from '@valueerror/ltjs-core/src/core/rules/model/Tag.js';
|
|
4
|
+
|
|
5
|
+
// Java: QuestionWhitespaceStrictRule
|
|
6
|
+
export class QuestionWhitespaceStrictRule extends QuestionWhitespaceRule {
|
|
7
|
+
static FQCN = 'org.languagetool.rules.fr.QuestionWhitespaceStrictRule';
|
|
8
|
+
|
|
9
|
+
// Java: QuestionWhitespaceStrictRule(ResourceBundle,Language)
|
|
10
|
+
constructor(messages, language) {
|
|
11
|
+
super(messages, language);
|
|
12
|
+
this.setTags([Tag.picky]);
|
|
13
|
+
this.setDefaultTempOff();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Java: QuestionWhitespaceStrictRule#getId()
|
|
17
|
+
getId() {
|
|
18
|
+
return 'FRENCH_WHITESPACE_STRICT';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Java: QuestionWhitespaceStrictRule#getDescription()
|
|
22
|
+
getDescription() {
|
|
23
|
+
return 'Insertion des espaces fines insécables';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Java: QuestionWhitespaceStrictRule#isAllowedWhitespaceChar(AnalyzedTokenReadings[],int)
|
|
27
|
+
isAllowedWhitespaceChar(tokens, i) {
|
|
28
|
+
// Strictly speaking, the character before ?!; should be an
|
|
29
|
+
// "espace fine insécable" (U+202f). In practise, an
|
|
30
|
+
// "espace insécable" (U+00a0) is also often used. Let's accept both.
|
|
31
|
+
if (i < 0) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
return tokens[i].getToken() === ' ' || tokens[i].getToken() === ' '
|
|
35
|
+
// covered by FRENCH_WHITESPACE (non strict). This makes both rules mutually exclusive
|
|
36
|
+
|| !tokens[i].isWhitespace();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// org.languagetool.rules.fr.SimpleReplaceRule
|
|
2
|
+
import { AbstractSimpleReplaceRule } from '@valueerror/ltjs-core/src/core/rules/AbstractSimpleReplaceRule.js';
|
|
3
|
+
import { Categories } from '@valueerror/ltjs-core/src/core/rules/model/Category.js';
|
|
4
|
+
import { ITSIssueType } from '@valueerror/ltjs-core/src/core/rules/model/ITSIssueType.js';
|
|
5
|
+
import { Locale } from '@valueerror/ltjs-core/src/lib/java/Locale.js';
|
|
6
|
+
|
|
7
|
+
let wrongWords = null; // Java: static wrongWords = loadFromPath("/fr/replace.txt", "/fr/replace_custom.txt")
|
|
8
|
+
const FR_LOCALE = new Locale('FR');
|
|
9
|
+
|
|
10
|
+
// Java: SimpleReplaceRule
|
|
11
|
+
export class SimpleReplaceRule extends AbstractSimpleReplaceRule {
|
|
12
|
+
static FQCN = 'org.languagetool.rules.fr.SimpleReplaceRule';
|
|
13
|
+
|
|
14
|
+
// Java: SimpleReplaceRule#getWrongWords()
|
|
15
|
+
getWrongWords() {
|
|
16
|
+
if (wrongWords === null) wrongWords = AbstractSimpleReplaceRule.loadFromPath('/fr/replace.txt', '/fr/replace_custom.txt');
|
|
17
|
+
return wrongWords;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Java: SimpleReplaceRule(ResourceBundle,Language)
|
|
21
|
+
constructor(messages, language) {
|
|
22
|
+
super(messages, language);
|
|
23
|
+
SimpleReplaceRule.prototype.getWrongWords.call(this); // Java: static initializer runs at class load
|
|
24
|
+
super.setCategory(Categories.TYPOS.getCategory(messages));
|
|
25
|
+
super.setLocQualityIssueType(ITSIssueType.Misspelling);
|
|
26
|
+
this.setIgnoreTaggedWords();
|
|
27
|
+
this.setCheckLemmas(false);
|
|
28
|
+
super.useSubRuleSpecificIds();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Java: SimpleReplaceRule#getId()
|
|
32
|
+
getId() {
|
|
33
|
+
return 'FR_SIMPLE_REPLACE_SIMPLE';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Java: SimpleReplaceRule#getDescription()
|
|
37
|
+
getDescription() {
|
|
38
|
+
return 'Mot incorrect : $match';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Java: SimpleReplaceRule#getShort()
|
|
42
|
+
getShort() {
|
|
43
|
+
return 'Mot incorrect';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Java: SimpleReplaceRule#getMessage(String,List)
|
|
47
|
+
getMessage(tokenStr, replacements) {
|
|
48
|
+
if (replacements.length > 0) {
|
|
49
|
+
return 'Vouliez-vous dire « ' + replacements[0] + ' » ?';
|
|
50
|
+
}
|
|
51
|
+
return this.getShort();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Java: SimpleReplaceRule#isCaseSensitive()
|
|
55
|
+
isCaseSensitive() {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Java: SimpleReplaceRule#getLocale()
|
|
60
|
+
getLocale() {
|
|
61
|
+
return FR_LOCALE;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// org.languagetool.rules.fr.SuggestionsFilter
|
|
2
|
+
import { registerClass } from '@valueerror/ltjs-core/src/core/resources/ClassBroker.js';
|
|
3
|
+
import { Pattern } from '@valueerror/ltjs-core/src/lib/java/regex/index.js';
|
|
4
|
+
import { RuleFilter } from '@valueerror/ltjs-core/src/core/patterns/RuleFilter.js';
|
|
5
|
+
|
|
6
|
+
// Java: SuggestionsFilter
|
|
7
|
+
export class SuggestionsFilter extends RuleFilter {
|
|
8
|
+
static FQCN = 'org.languagetool.rules.fr.SuggestionsFilter';
|
|
9
|
+
static JAVA_ABSTRACT = false;
|
|
10
|
+
static JAVA_CONSTRUCTORS = [{ params: [], text: 'public org.languagetool.rules.fr.SuggestionsFilter()' }];
|
|
11
|
+
|
|
12
|
+
// Java: SuggestionsFilter#acceptRuleMatch(RuleMatch,Map,int,AnalyzedTokenReadings[],List)
|
|
13
|
+
acceptRuleMatch(match, arguments_) {
|
|
14
|
+
const removeSuggestionsRegexp = this.getRequired('RemoveSuggestionsRegexp', arguments_);
|
|
15
|
+
const ruleMatch = match;
|
|
16
|
+
const replacements = match.getSuggestedReplacements();
|
|
17
|
+
const newReplacements = [];
|
|
18
|
+
const p = Pattern.compile(removeSuggestionsRegexp, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
|
19
|
+
for (const r of replacements) {
|
|
20
|
+
if (!p.matcher(r).matches()) {
|
|
21
|
+
newReplacements.push(r);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
ruleMatch.setSuggestedReplacements(newReplacements);
|
|
25
|
+
return ruleMatch;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
registerClass(SuggestionsFilter.FQCN, SuggestionsFilter);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// org.languagetool.rules.fr.WordWithDeterminerFilter
|
|
2
|
+
import { registerClass } from '@valueerror/ltjs-core/src/core/resources/ClassBroker.js';
|
|
3
|
+
import { IllegalArgumentException, RuntimeException } from '@valueerror/ltjs-core/src/lib/java/errors.js';
|
|
4
|
+
import { Integer } from '@valueerror/ltjs-core/src/lib/java/int.js';
|
|
5
|
+
import { listGet } from '@valueerror/ltjs-core/src/lib/java/arrays.js';
|
|
6
|
+
import { Pattern } from '@valueerror/ltjs-core/src/lib/java/regex/index.js';
|
|
7
|
+
import { equalsIgnoreCase, javaMatches, javaReplace, toUpperCase } from '@valueerror/ltjs-core/src/lib/java/strings.js';
|
|
8
|
+
import { Languages } from '@valueerror/ltjs-core/src/core/Languages.js';
|
|
9
|
+
import { RuleMatch } from '@valueerror/ltjs-core/src/core/rules/model/RuleMatch.js';
|
|
10
|
+
import { RuleFilter } from '@valueerror/ltjs-core/src/core/patterns/RuleFilter.js';
|
|
11
|
+
import { isAllUppercase, isCapitalizedWord, uppercaseFirstChar } from '@valueerror/ltjs-core/src/core/text/StringTools.js';
|
|
12
|
+
import { FrenchSynthesizer } from '../synthesis/FrenchSynthesizer.js';
|
|
13
|
+
|
|
14
|
+
const detPattern = Pattern.compile('(P.)?D .*|J .*|V.* ppa .*'); // Java: WordWithDeterminerFilter#detPattern
|
|
15
|
+
const wordPattern = Pattern.compile('[ZNJ] .*|V.* ppa .*'); // Java: WordWithDeterminerFilter#wordPattern
|
|
16
|
+
// 0=MS, 1=FS, 2=MP, 3=FP
|
|
17
|
+
const genderNumber = ['([me]) (s|sp)', '([fe]) (s|sp)', '([me]) (p|sp)', '([fe]) (p|sp)']; // Java: WordWithDeterminerFilter#genderNumber
|
|
18
|
+
const determiner = '((P.)?D |J |V.* ppa )'; // Java: WordWithDeterminerFilter#determiner
|
|
19
|
+
const exceptionsDeterminer = ['bels', 'fols', 'mols', 'nouvels']; // Java: WordWithDeterminerFilter#exceptionsDeterminer
|
|
20
|
+
const categoryToCheck = 'CAT_ELISION'; // Java: WordWithDeterminerFilter#categoryToCheck
|
|
21
|
+
const rulesToCheck = ['CET_CE', 'CE_CET', 'MA_VOYELLE', 'MON_NFS', 'VIEUX']; // Java: WordWithDeterminerFilter#rulesToCheck
|
|
22
|
+
|
|
23
|
+
// Java: WordWithDeterminerFilter (create suggestions for: determiner + noun/adjective)
|
|
24
|
+
export class WordWithDeterminerFilter extends RuleFilter {
|
|
25
|
+
static FQCN = 'org.languagetool.rules.fr.WordWithDeterminerFilter';
|
|
26
|
+
static JAVA_ABSTRACT = false;
|
|
27
|
+
static JAVA_CONSTRUCTORS = [{ params: [], text: 'public org.languagetool.rules.fr.WordWithDeterminerFilter()' }];
|
|
28
|
+
|
|
29
|
+
// Java: WordWithDeterminerFilter#acceptRuleMatch(RuleMatch,Map,int,AnalyzedTokenReadings[],List)
|
|
30
|
+
acceptRuleMatch(match, arguments_, patternTokenPos, patternTokens) {
|
|
31
|
+
const lt = Languages.getLanguageForShortCode('fr').createDefaultJLanguageTool();
|
|
32
|
+
const wordFrom = this.getRequired('wordFrom', arguments_);
|
|
33
|
+
const determinerFrom = this.getRequired('determinerFrom', arguments_);
|
|
34
|
+
let posWord = 0;
|
|
35
|
+
let posDeterminer = 0;
|
|
36
|
+
if (wordFrom !== null && determinerFrom !== null) {
|
|
37
|
+
posWord = Integer.parseInt(wordFrom);
|
|
38
|
+
if (posWord < 1 || posWord > patternTokens.length) {
|
|
39
|
+
throw new IllegalArgumentException('WordWithDeterminerFilter: Index out of bounds in '
|
|
40
|
+
+ match.getRule().getFullId() + ', wordFrom: ' + posWord);
|
|
41
|
+
}
|
|
42
|
+
posDeterminer = Integer.parseInt(determinerFrom);
|
|
43
|
+
if (posDeterminer < 1 || posDeterminer > patternTokens.length) {
|
|
44
|
+
throw new IllegalArgumentException('WordWithDeterminerFilter: Index out of bounds in '
|
|
45
|
+
+ match.getRule().getFullId() + ', posDeterminer: ' + posWord);
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
throw new IllegalArgumentException('WordWithDeterminerFilter: undefined parameters wordFrom or determinerFrom in '
|
|
49
|
+
+ match.getRule().getFullId());
|
|
50
|
+
}
|
|
51
|
+
const atrDeterminer = patternTokens[posDeterminer - 1];
|
|
52
|
+
const atrWord = patternTokens[posWord - 1];
|
|
53
|
+
const isDeterminerCapitalized = isCapitalizedWord(atrDeterminer.getToken());
|
|
54
|
+
const isWordCapitalized = isCapitalizedWord(atrWord.getToken());
|
|
55
|
+
const isDeterminerAllupper = isAllUppercase(atrDeterminer.getToken())
|
|
56
|
+
&& !equalsIgnoreCase(atrDeterminer.getToken(), "L'");
|
|
57
|
+
const isWordAllupper = isAllUppercase(atrWord.getToken());
|
|
58
|
+
const atDeterminer = this.getAnalyzedToken(atrDeterminer, detPattern);
|
|
59
|
+
const atWord = this.getAnalyzedToken(atrWord, wordPattern);
|
|
60
|
+
if (atWord === null || atDeterminer === null) {
|
|
61
|
+
throw new RuntimeException(
|
|
62
|
+
"Error analyzing sentence: '" + match.getSentence().getText() + "' with rule " + match.getRule().getFullId());
|
|
63
|
+
}
|
|
64
|
+
const isNoun = atWord.getPOSTag().startsWith('N') || atWord.getPOSTag().startsWith('Z');
|
|
65
|
+
const isAdjective = atWord.getPOSTag().startsWith('J');
|
|
66
|
+
|
|
67
|
+
let prefix = '[ZNJ] ';
|
|
68
|
+
if (isNoun && !isAdjective) {
|
|
69
|
+
prefix = '[NZ] ';
|
|
70
|
+
} else if (!isNoun && isAdjective) {
|
|
71
|
+
prefix = 'J ';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// synthesize all forms
|
|
75
|
+
const determinerForms = new Array(4);
|
|
76
|
+
const wordForms = new Array(4);
|
|
77
|
+
for (let i = 0; i < 4; i++) {
|
|
78
|
+
determinerForms[i] = FrenchSynthesizer.INSTANCE.synthesize(atDeterminer, determiner + genderNumber[i], true);
|
|
79
|
+
wordForms[i] = FrenchSynthesizer.INSTANCE.synthesize(atWord, prefix + genderNumber[i], true);
|
|
80
|
+
// if it cannot be synthesyzed, keep the original determiner
|
|
81
|
+
if (determinerForms[i].length === 0 && javaMatches(atDeterminer.getPOSTag(), '.+' + genderNumber[i])) {
|
|
82
|
+
determinerForms[i] = [atDeterminer.getToken()];
|
|
83
|
+
}
|
|
84
|
+
// if it cannot be synthesyzed, keep the original word
|
|
85
|
+
if (wordForms[i].length === 0 && javaMatches(atWord.getPOSTag(), '.+' + genderNumber[i])) {
|
|
86
|
+
wordForms[i] = [atWord.getToken()];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// generate suggestions
|
|
90
|
+
const replacements = [];
|
|
91
|
+
for (let i = 0; i < 4; i++) {
|
|
92
|
+
for (let cw = 0; cw < wordForms[i].length; cw++) {
|
|
93
|
+
for (let cd = 0; cd < determinerForms[i].length; cd++) {
|
|
94
|
+
if (exceptionsDeterminer.includes(determinerForms[i][cd])) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (determinerForms[i][cd] != null && wordForms[i][cw] != null) {
|
|
98
|
+
let det = determinerForms[i][cd];
|
|
99
|
+
let word = wordForms[i][cw];
|
|
100
|
+
if (isDeterminerCapitalized) {
|
|
101
|
+
det = uppercaseFirstChar(det);
|
|
102
|
+
}
|
|
103
|
+
if (isWordCapitalized) {
|
|
104
|
+
word = uppercaseFirstChar(word);
|
|
105
|
+
}
|
|
106
|
+
if (isDeterminerAllupper) {
|
|
107
|
+
det = toUpperCase(det);
|
|
108
|
+
}
|
|
109
|
+
if (isWordAllupper) {
|
|
110
|
+
word = toUpperCase(word);
|
|
111
|
+
}
|
|
112
|
+
let r = det + ' ' + word;
|
|
113
|
+
r = javaReplace(r, "' ", "'");
|
|
114
|
+
// remove suggestions with errors
|
|
115
|
+
if (this.suggestionHasNoErrors(r, lt) && !replacements.includes(r)) {
|
|
116
|
+
if (r.endsWith(atWord.getToken())) {
|
|
117
|
+
replacements.unshift(r);
|
|
118
|
+
} else {
|
|
119
|
+
replacements.push(r);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const message = match.getMessage();
|
|
127
|
+
const ruleMatch = new RuleMatch(match.getRule(), match.getSentence(), match.getFromPos(), match.getToPos(),
|
|
128
|
+
message, match.getShortMessage());
|
|
129
|
+
ruleMatch.setType(match.getType());
|
|
130
|
+
// add existing suggestion in the XML rule
|
|
131
|
+
replacements.splice(0, 0, ...match.getSuggestedReplacements());
|
|
132
|
+
if (replacements.length > 0) {
|
|
133
|
+
ruleMatch.setSuggestedReplacements(replacements);
|
|
134
|
+
}
|
|
135
|
+
return ruleMatch;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Java: WordWithDeterminerFilter#suggestionHasNoErrors(String,JLanguageTool)
|
|
139
|
+
suggestionHasNoErrors(newSuggestion, lt) {
|
|
140
|
+
const analyzedSentence = listGet(lt.analyzeText(newSuggestion), 0);
|
|
141
|
+
for (const r of lt.getAllActiveRules()) {
|
|
142
|
+
if (r.getCategory().getId().toString() === categoryToCheck || rulesToCheck.includes(r.getId())) {
|
|
143
|
+
const matches = r.match(analyzedSentence);
|
|
144
|
+
if (matches.length > 0) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Java: WordWithDeterminerFilter#getAnalyzedToken(AnalyzedTokenReadings,Pattern)
|
|
153
|
+
getAnalyzedToken(aToken, pattern) {
|
|
154
|
+
for (const analyzedToken of aToken) {
|
|
155
|
+
let posTag = analyzedToken.getPOSTag();
|
|
156
|
+
if (posTag === null) {
|
|
157
|
+
posTag = 'UNKNOWN';
|
|
158
|
+
}
|
|
159
|
+
const m = pattern.matcher(posTag);
|
|
160
|
+
if (m.matches()) {
|
|
161
|
+
return analyzedToken;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
registerClass(WordWithDeterminerFilter.FQCN, WordWithDeterminerFilter);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Java: org.languagetool.synthesis.FrenchSynthesizer
|
|
2
|
+
import { registerComponent, sharedComponent } from '@valueerror/ltjs-core/src/core/language/components.js';
|
|
3
|
+
import { BaseSynthesizer } from '@valueerror/ltjs-core/src/core/tagging/synthesis/BaseSynthesizer.js';
|
|
4
|
+
import { toLowerCase } from '@valueerror/ltjs-core/src/lib/java/strings.js';
|
|
5
|
+
|
|
6
|
+
const exceptionsEgrave = ['burkinabè', 'koinè', 'épistémè'];
|
|
7
|
+
|
|
8
|
+
// Java: FrenchSynthesizer
|
|
9
|
+
export class FrenchSynthesizer extends BaseSynthesizer {
|
|
10
|
+
static FQCN = 'org.languagetool.synthesis.FrenchSynthesizer';
|
|
11
|
+
|
|
12
|
+
// Java: FrenchSynthesizer#INSTANCE
|
|
13
|
+
static get INSTANCE() {
|
|
14
|
+
return sharedComponent(FrenchSynthesizer.FQCN);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Java: FrenchSynthesizer#FrenchSynthesizer() (the deprecated (Language) constructor delegates here)
|
|
18
|
+
constructor() {
|
|
19
|
+
super('fr/fr.sor', '/fr/french_synth.dict', '/fr/french_tags.txt', 'fr');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Java: FrenchSynthesizer#isException(String)
|
|
23
|
+
isException(w) {
|
|
24
|
+
// remove: qq, qqe...
|
|
25
|
+
if (w.startsWith('qq')) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
// informè V ind pres 1 s
|
|
29
|
+
if (w.endsWith('è') && !exceptionsEgrave.includes(toLowerCase(w))) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
registerComponent(FrenchSynthesizer.FQCN, () => new FrenchSynthesizer());
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// org.languagetool.tagging.disambiguation.fr.FrenchHybridDisambiguator (spelling_global chunker, multiword chunker, then XML rules)
|
|
2
|
+
import { registerComponent } from '@valueerror/ltjs-core/src/core/language/components.js';
|
|
3
|
+
import { AbstractDisambiguator } from '@valueerror/ltjs-core/src/core/tagging/disambiguation/AbstractDisambiguator.js';
|
|
4
|
+
import { MultiWordChunker } from '@valueerror/ltjs-core/src/core/tagging/disambiguation/MultiWordChunker.js';
|
|
5
|
+
import { XmlRuleDisambiguator } from '@valueerror/ltjs-core/src/core/disambiguation/XmlRuleDisambiguator.js';
|
|
6
|
+
import { French } from '../language/French.js';
|
|
7
|
+
|
|
8
|
+
// Java: FrenchHybridDisambiguator
|
|
9
|
+
export class FrenchHybridDisambiguator extends AbstractDisambiguator {
|
|
10
|
+
static FQCN = 'org.languagetool.tagging.disambiguation.fr.FrenchHybridDisambiguator';
|
|
11
|
+
|
|
12
|
+
// Java: FrenchHybridDisambiguator#FrenchHybridDisambiguator() (field initialisers run before the constructor body)
|
|
13
|
+
constructor() {
|
|
14
|
+
super();
|
|
15
|
+
this.chunker = MultiWordChunker.getInstance('/fr/multiwords.txt', true, true, false);
|
|
16
|
+
this.disambiguator = new XmlRuleDisambiguator(French.getInstance(), true);
|
|
17
|
+
this.chunkerGlobal = MultiWordChunker.getInstance('/spelling_global.txt', false, true, false, MultiWordChunker.tagForNotAddingTags);
|
|
18
|
+
this.chunker.setRemovePreviousTags(true);
|
|
19
|
+
this.chunkerGlobal.setIgnoreSpelling(true);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Java: FrenchHybridDisambiguator#disambiguate(AnalyzedSentence) / disambiguate(AnalyzedSentence,CheckCancelledCallback)
|
|
23
|
+
disambiguate(input, checkCanceled = null) {
|
|
24
|
+
return this.disambiguator.disambiguate(this.chunker.disambiguate(this.chunkerGlobal.disambiguate(input, checkCanceled), checkCanceled), checkCanceled);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
registerComponent(FrenchHybridDisambiguator.FQCN, () => new FrenchHybridDisambiguator());
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// Java: org.languagetool.tagging.fr.FrenchTagger
|
|
2
|
+
import { NullPointerException } from '@valueerror/ltjs-core/src/lib/java/errors.js';
|
|
3
|
+
import { Locale } from '@valueerror/ltjs-core/src/lib/java/Locale.js';
|
|
4
|
+
import { Pattern } from '@valueerror/ltjs-core/src/lib/java/regex/index.js';
|
|
5
|
+
import { toLowerCase } from '@valueerror/ltjs-core/src/lib/java/strings.js';
|
|
6
|
+
import { AnalyzedToken } from '@valueerror/ltjs-core/src/core/AnalyzedToken.js';
|
|
7
|
+
import { AnalyzedTokenReadings } from '@valueerror/ltjs-core/src/core/AnalyzedTokenReadings.js';
|
|
8
|
+
import { ChunkTag } from '@valueerror/ltjs-core/src/core/chunking/ChunkTag.js';
|
|
9
|
+
import { registerComponent, sharedComponent } from '@valueerror/ltjs-core/src/core/language/components.js';
|
|
10
|
+
import { BaseTagger } from '@valueerror/ltjs-core/src/core/tagging/BaseTagger.js';
|
|
11
|
+
import { convertToTitleCaseIteratingChars, isAllUppercase, isCapitalizedWord, isEmoji } from '@valueerror/ltjs-core/src/core/text/StringTools.js';
|
|
12
|
+
|
|
13
|
+
const VERB = Pattern.compile('V .+');
|
|
14
|
+
const PREFIXES_FOR_VERBS = Pattern.compile(
|
|
15
|
+
'(auto|auto-|re-|sur-)([^-].*[aeiouêàéèíòóïü].+[aeiouêàéèíòóïü].*)', Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
|
16
|
+
const NOUN_ADJ = Pattern.compile('[NJ] .+|V ppa.*');
|
|
17
|
+
const PREFIXES_NOUN_ADJ = Pattern.compile(
|
|
18
|
+
'(post-|sur-|mini-|méga-|demi-|péri-|anti-|géo-|nord-|sud-|néo-|méga-|ultra-|pro-|inter-|micro-|macro-|sous-|haut-|auto-|ré-|pré-|super-|vice-|hyper-|proto-|grand-|pseudo-)(.+)',
|
|
19
|
+
Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
|
20
|
+
const PREFIXES_FOR_NOUN_ADJ = Pattern.compile(
|
|
21
|
+
'(mini|méga)([^-].*[aeiouêàéèíòóïü].+[aeiouêàéèíòóïü].*)', Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
|
22
|
+
|
|
23
|
+
const AMBIGOUS_TOKENS = ['-Le', '-Les', '-La', '-Elle', '-Elles', '-On', '-Tu', '-Vous', '-Il', '-Ils', '-Ce'];
|
|
24
|
+
|
|
25
|
+
// Java: FrenchTagger
|
|
26
|
+
export class FrenchTagger extends BaseTagger {
|
|
27
|
+
static FQCN = 'org.languagetool.tagging.fr.FrenchTagger';
|
|
28
|
+
|
|
29
|
+
// Java: FrenchTagger#INSTANCE
|
|
30
|
+
static get INSTANCE() {
|
|
31
|
+
return sharedComponent(FrenchTagger.FQCN);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Java: FrenchTagger#FrenchTagger()
|
|
35
|
+
constructor() {
|
|
36
|
+
super('/fr/french.dict', Locale.FRENCH, false);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Java: FrenchTagger#overwriteWithManualTagger()
|
|
40
|
+
overwriteWithManualTagger() {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Java: FrenchTagger#tag(List)
|
|
45
|
+
tag(sentenceTokens) {
|
|
46
|
+
const tokenReadings = [];
|
|
47
|
+
let pos = 0;
|
|
48
|
+
for (let word of sentenceTokens) {
|
|
49
|
+
if (word === null || word === undefined) throw new NullPointerException('Cannot invoke "String.length()" because "word" is null');
|
|
50
|
+
let containsTypewriterApostrophe = false;
|
|
51
|
+
let containsTypographicApostrophe = false;
|
|
52
|
+
if (word.length > 1) {
|
|
53
|
+
if (word.includes("'")) {
|
|
54
|
+
containsTypewriterApostrophe = true;
|
|
55
|
+
}
|
|
56
|
+
if (word.includes('’')) {
|
|
57
|
+
containsTypographicApostrophe = true;
|
|
58
|
+
word = word.replaceAll('’', "'");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
let l = this.tagWord(word, word);
|
|
62
|
+
if (l.length === 0 && toLowerCase(word).includes('oe')) {
|
|
63
|
+
l = this.tagWord(word.replaceAll('oe', 'œ').replaceAll('OE', 'Œ'), word);
|
|
64
|
+
}
|
|
65
|
+
if (l.length === 0 && isEmoji(word)) {
|
|
66
|
+
l.push(new AnalyzedToken(word, '_emoji_', '_emoji_'));
|
|
67
|
+
}
|
|
68
|
+
if (l.length === 0) {
|
|
69
|
+
l.push(new AnalyzedToken(word, null, null));
|
|
70
|
+
}
|
|
71
|
+
const atr = new AnalyzedTokenReadings(l, pos);
|
|
72
|
+
if (containsTypewriterApostrophe) {
|
|
73
|
+
atr.setChunkTags([new ChunkTag('containsTypewriterApostrophe')]);
|
|
74
|
+
}
|
|
75
|
+
if (containsTypographicApostrophe) {
|
|
76
|
+
atr.setChunkTags([new ChunkTag('containsTypographicApostrophe')]);
|
|
77
|
+
}
|
|
78
|
+
tokenReadings.push(atr);
|
|
79
|
+
pos += word.length;
|
|
80
|
+
}
|
|
81
|
+
return tokenReadings;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Java: FrenchTagger#tagWord(String,String)
|
|
85
|
+
tagWord(word, originalWord) {
|
|
86
|
+
const l = [];
|
|
87
|
+
const lowerWord = toLowerCase(word, this.locale);
|
|
88
|
+
const isStartUpper = isCapitalizedWord(word);
|
|
89
|
+
const isAllUpper = isAllUppercase(word);
|
|
90
|
+
const isHyphenatedTitleCase = !AMBIGOUS_TOKENS.includes(originalWord)
|
|
91
|
+
&& originalWord.includes('-') && originalWord === convertToTitleCaseIteratingChars(lowerWord);
|
|
92
|
+
l.push(...this.asAnalyzedTokenListForTaggedWords(originalWord, this.getWordTagger().tag(word)));
|
|
93
|
+
if (isAllUpper || isStartUpper || isHyphenatedTitleCase) {
|
|
94
|
+
l.push(...this.asAnalyzedTokenListForTaggedWords(originalWord, this.getWordTagger().tag(lowerWord)));
|
|
95
|
+
}
|
|
96
|
+
if (l.length === 0 && isAllUpper) {
|
|
97
|
+
const firstUpper = convertToTitleCaseIteratingChars(lowerWord);
|
|
98
|
+
l.push(...this.asAnalyzedTokenListForTaggedWords(originalWord, this.getWordTagger().tag(firstUpper)));
|
|
99
|
+
}
|
|
100
|
+
if (l.length === 0) {
|
|
101
|
+
const add = this.additionalTags(word);
|
|
102
|
+
if (add !== null) l.push(...add);
|
|
103
|
+
}
|
|
104
|
+
return l;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Java: FrenchTagger#additionalTags(String)
|
|
108
|
+
additionalTags(word) {
|
|
109
|
+
let additionalTaggedTokens = [];
|
|
110
|
+
let matcher = PREFIXES_FOR_VERBS.matcher(word);
|
|
111
|
+
if (matcher.matches()) {
|
|
112
|
+
const possibleVerb = toLowerCase(matcher.group(2));
|
|
113
|
+
const taggerTokens = this.asAnalyzedTokenListForTaggedWords(word, this.getWordTagger().tag(possibleVerb));
|
|
114
|
+
for (const taggerToken of taggerTokens) {
|
|
115
|
+
const posTag = taggerToken.getPOSTag();
|
|
116
|
+
if (posTag !== null) {
|
|
117
|
+
if (VERB.matcher(posTag).matches()) {
|
|
118
|
+
const lemma = toLowerCase(matcher.group(1)) + taggerToken.getLemma();
|
|
119
|
+
additionalTaggedTokens.push(new AnalyzedToken(word, posTag, lemma));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (additionalTaggedTokens.length > 0) {
|
|
124
|
+
return additionalTaggedTokens;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
matcher = PREFIXES_FOR_NOUN_ADJ.matcher(word);
|
|
128
|
+
if (matcher.matches()) {
|
|
129
|
+
const possibleNoun = toLowerCase(matcher.group(2));
|
|
130
|
+
const taggerTokens = this.asAnalyzedTokenListForTaggedWords(word, this.getWordTagger().tag(possibleNoun));
|
|
131
|
+
for (const taggerToken of taggerTokens) {
|
|
132
|
+
const posTag = taggerToken.getPOSTag();
|
|
133
|
+
if (posTag !== null) {
|
|
134
|
+
if (NOUN_ADJ.matcher(posTag).matches()) {
|
|
135
|
+
const lemma = toLowerCase(matcher.group(1)) + taggerToken.getLemma();
|
|
136
|
+
additionalTaggedTokens.push(new AnalyzedToken(word, posTag, lemma));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (additionalTaggedTokens.length > 0) {
|
|
141
|
+
return additionalTaggedTokens;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
matcher = PREFIXES_NOUN_ADJ.matcher(word);
|
|
145
|
+
if (matcher.matches()) {
|
|
146
|
+
const possibleNoun = toLowerCase(matcher.group(2));
|
|
147
|
+
let taggerTokens = this.asAnalyzedTokenListForTaggedWords(word, this.getWordTagger().tag(possibleNoun));
|
|
148
|
+
for (const taggerToken of taggerTokens) {
|
|
149
|
+
const posTag = taggerToken.getPOSTag();
|
|
150
|
+
if (posTag !== null) {
|
|
151
|
+
if (NOUN_ADJ.matcher(posTag).matches()) {
|
|
152
|
+
const lemma = toLowerCase(matcher.group(1)) + taggerToken.getLemma();
|
|
153
|
+
additionalTaggedTokens.push(new AnalyzedToken(word, posTag, lemma));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (additionalTaggedTokens.length === 0) {
|
|
158
|
+
taggerTokens = this.asAnalyzedTokenListForTaggedWords(word, this.getWordTagger().tag(toLowerCase(possibleNoun)));
|
|
159
|
+
for (const taggerToken of taggerTokens) {
|
|
160
|
+
const posTag = taggerToken.getPOSTag();
|
|
161
|
+
if (posTag !== null) {
|
|
162
|
+
if (NOUN_ADJ.matcher(posTag).matches()) {
|
|
163
|
+
const lemma = toLowerCase(matcher.group(1)) + taggerToken.getLemma();
|
|
164
|
+
additionalTaggedTokens.push(new AnalyzedToken(word, posTag, lemma));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return additionalTaggedTokens;
|
|
170
|
+
}
|
|
171
|
+
additionalTaggedTokens = null;
|
|
172
|
+
return additionalTaggedTokens;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
registerComponent(FrenchTagger.FQCN, () => new FrenchTagger());
|