@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.
Files changed (69) hide show
  1. package/LICENSE +232 -0
  2. package/NOTICE +9 -0
  3. package/README.md +25 -2
  4. package/index.js +24 -0
  5. package/licenses/LGPL-2.1.txt +504 -0
  6. package/licenses/french-pos-dict.pom.xml +115 -0
  7. package/licenses/resource_fr_README_lexique.txt +30 -0
  8. package/licenses/resource_fr_hunspell_README_fr.txt +73 -0
  9. package/package.json +60 -4
  10. package/resource-root.js +4 -0
  11. package/resources/META-INF/org/languagetool/language-module.properties +1 -0
  12. package/resources/org/languagetool/resource/fr/README_lexique.txt +30 -0
  13. package/resources/org/languagetool/resource/fr/added.txt +477 -0
  14. package/resources/org/languagetool/resource/fr/added_custom.txt +4 -0
  15. package/resources/org/languagetool/resource/fr/compounds.txt +1443 -0
  16. package/resources/org/languagetool/resource/fr/confusion_sets.txt +76 -0
  17. package/resources/org/languagetool/resource/fr/disambiguation.xml +11391 -0
  18. package/resources/org/languagetool/resource/fr/fr.sor +162 -0
  19. package/resources/org/languagetool/resource/fr/french.dict +0 -0
  20. package/resources/org/languagetool/resource/fr/french.info +20 -0
  21. package/resources/org/languagetool/resource/fr/french_synth.dict +0 -0
  22. package/resources/org/languagetool/resource/fr/french_synth.info +4 -0
  23. package/resources/org/languagetool/resource/fr/french_tags.txt +268 -0
  24. package/resources/org/languagetool/resource/fr/hunspell/README_fr.txt +73 -0
  25. package/resources/org/languagetool/resource/fr/hunspell/ignore.txt +1551 -0
  26. package/resources/org/languagetool/resource/fr/hunspell/prohibit.txt +3 -0
  27. package/resources/org/languagetool/resource/fr/hunspell/spelling.txt +34735 -0
  28. package/resources/org/languagetool/resource/fr/hunspell/spelling_custom.txt +1 -0
  29. package/resources/org/languagetool/resource/fr/hyphenated_words.txt +12864 -0
  30. package/resources/org/languagetool/resource/fr/multiwords.txt +4886 -0
  31. package/resources/org/languagetool/resource/fr/removed.txt +5 -0
  32. package/resources/org/languagetool/resource/fr/removed_custom.txt +4 -0
  33. package/resources/org/languagetool/rules/fr/grammar.xml +151570 -0
  34. package/resources/org/languagetool/rules/fr/remote-rule-filters.xml +820 -0
  35. package/resources/org/languagetool/rules/fr/replace.txt +39 -0
  36. package/resources/org/languagetool/rules/fr/replace_custom.txt +7 -0
  37. package/resources/org/languagetool/rules/fr/style.xml +22790 -0
  38. package/resources/org/languagetool/rules/fr/synonyms.txt +203 -0
  39. package/src/lang/fr/language/BelgianFrench.js +38 -0
  40. package/src/lang/fr/language/CanadianFrench.js +38 -0
  41. package/src/lang/fr/language/French.js +478 -0
  42. package/src/lang/fr/language/SwissFrench.js +38 -0
  43. package/src/lang/fr/language/id2prioFrench.js +102 -0
  44. package/src/lang/fr/rules/AdvancedSynthesizerFilter.js +12 -0
  45. package/src/lang/fr/rules/CompoundRule.js +47 -0
  46. package/src/lang/fr/rules/DMYDateCheckFilter.js +31 -0
  47. package/src/lang/fr/rules/DateCheckFilter.js +35 -0
  48. package/src/lang/fr/rules/DateFilterHelper.js +55 -0
  49. package/src/lang/fr/rules/FindSuggestionsFilter.js +80 -0
  50. package/src/lang/fr/rules/FrenchLazy.js +9 -0
  51. package/src/lang/fr/rules/FrenchMultitokenSpeller.js +15 -0
  52. package/src/lang/fr/rules/FrenchNumberInWordFilter.js +29 -0
  53. package/src/lang/fr/rules/FrenchPartialPosTagFilter.js +29 -0
  54. package/src/lang/fr/rules/FrenchRepeatedWordsRule.js +76 -0
  55. package/src/lang/fr/rules/FrenchSuppressMisspelledSuggestionsFilter.js +12 -0
  56. package/src/lang/fr/rules/InterrogativeVerbFilter.js +159 -0
  57. package/src/lang/fr/rules/MakeContractionsFilter.js +32 -0
  58. package/src/lang/fr/rules/MorfologikFrenchSpellerRule.js +262 -0
  59. package/src/lang/fr/rules/NewYearDateFilter.js +30 -0
  60. package/src/lang/fr/rules/PostponedAdjectiveConcordanceFilter.js +512 -0
  61. package/src/lang/fr/rules/QuestionWhitespaceRule.js +171 -0
  62. package/src/lang/fr/rules/QuestionWhitespaceStrictRule.js +38 -0
  63. package/src/lang/fr/rules/SimpleReplaceRule.js +63 -0
  64. package/src/lang/fr/rules/SuggestionsFilter.js +29 -0
  65. package/src/lang/fr/rules/WordWithDeterminerFilter.js +168 -0
  66. package/src/lang/fr/synthesis/FrenchSynthesizer.js +36 -0
  67. package/src/lang/fr/tagging/FrenchHybridDisambiguator.js +28 -0
  68. package/src/lang/fr/tagging/FrenchTagger.js +176 -0
  69. 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());