From 2b87c919bcd879c7f356308bc522b95f33e35f3b Mon Sep 17 00:00:00 2001 From: StefanVukovic99 Date: Sat, 20 Jan 2024 02:25:23 +0100 Subject: [PATCH] Dictionary deinflections (#503) * wip * wip * fix v3 * wip * fix tests * fix maxitems * hide deinflection definitions * fix anki template * undo unnecessary change * delete console.log * refactor * add set false to handlebars * lint * fix tests * fix comments * fix * use Map in areArraysEqualIgnoreOrder * move inflection source icons to css * lint * improve naming * fix tests * add test * typescript * use for of * wip * comments * anki template upgrade * update descriptions --- ext/css/display.css | 30 +- .../dictionary-term-bank-v3-schema.json | 20 +- ext/data/schemas/options-schema.json | 7 +- ...nki-field-templates-upgrade-v24.handlebars | 52 ++ .../default-anki-field-templates.handlebars | 22 +- ext/display-templates.html | 3 +- ext/js/background/backend.js | 8 +- ext/js/data/options-util.js | 17 +- ext/js/data/sandbox/anki-note-data-creator.js | 4 +- ext/js/dictionary/dictionary-importer.js | 2 +- ext/js/display/display-generator.js | 46 +- ext/js/language/deinflector.js | 2 +- ext/js/language/translator.js | 235 ++++- .../pages/settings/dictionary-controller.js | 10 +- .../sandbox/anki-template-renderer.js | 2 +- ext/settings.html | 18 + test/data/anki-note-builder-test-results.json | 36 + test/data/database-test-cases.json | 6 +- .../valid-dictionary1/term_bank_1.json | 4 +- test/data/translator-test-inputs.json | 10 +- .../translator-test-results-note-data1.json | 791 ++++++++++++++--- test/data/translator-test-results.json | 837 +++++++++++++++--- test/options-util.test.js | 5 +- types/ext/anki-templates.d.ts | 2 +- types/ext/dictionary-data.d.ts | 9 + types/ext/dictionary.d.ts | 15 +- types/ext/settings.d.ts | 1 + types/ext/translation-internal.d.ts | 5 +- types/ext/translation.d.ts | 4 + 29 files changed, 1861 insertions(+), 342 deletions(-) create mode 100644 ext/data/templates/anki-field-templates-upgrade-v24.handlebars diff --git a/ext/css/display.css b/ext/css/display.css index 49aeaaa5d0..e0b7ab6d05 100644 --- a/ext/css/display.css +++ b/ext/css/display.css @@ -809,17 +809,39 @@ button.action-button:active { /* Inflections */ -.inflection-list { - display: inline-block; +.inflection-rule-chains { + padding-inline-start: 0; + list-style-type: none; +} +.inflection-rule-chain { color: var(--reason-text-color); } -.inflection-list:empty { +.inflection-rule-chain:empty { display: none; } -.inflection-list>.inflection+.inflection-separator+.inflection::before { +.inflection-rule-chain>.inflection+.inflection-separator+.inflection::before { content: var(--inflection-separator); padding: 0 0.25em; } +.inflection-source-icon { + display: inline-block; + white-space: nowrap; + text-align: center; + width: 1.4em; + margin-right: 0.2em; +} +.inflection-source-icon[data-inflection-source='dictionary']::after { + content: '📖'; +} +.inflection-source-icon[data-inflection-source='algorithm']::after { + content: '🧩'; +} +.inflection-source-icon[data-inflection-source='both'] { + width: 2.8em; +} +.inflection-source-icon[data-inflection-source='both']::after { + content: '🧩📖'; +} /* Headwords */ diff --git a/ext/data/schemas/dictionary-term-bank-v3-schema.json b/ext/data/schemas/dictionary-term-bank-v3-schema.json index 8243f2a7f5..066229c304 100644 --- a/ext/data/schemas/dictionary-term-bank-v3-schema.json +++ b/ext/data/schemas/dictionary-term-bank-v3-schema.json @@ -400,7 +400,7 @@ }, { "type": "string", - "description": "String of space-separated rule identifiers for the definition which is used to validate delinflection. Valid rule identifiers are: v1: ichidan verb; v5: godan verb; vs: suru verb; vk: kuru verb; adj-i: i-adjective. An empty string corresponds to words which aren't inflected, such as nouns." + "description": "String of space-separated rule identifiers for the definition which is used to validate deinflection. An empty string should be used for words which aren't inflected." }, { "type": "number", @@ -535,6 +535,24 @@ } } ] + }, + { + "type": "array", + "description": "Deinflection of the term to an uninflected term.", + "items": [ + { + "type": "string", + "description": "The uninflected term." + }, + { + "type": "array", + "description": "A chain of inflection rules that produced the inflected term", + "items": { + "type": "string", + "description": "A single inflection rule." + } + } + ] } ] } diff --git a/ext/data/schemas/options-schema.json b/ext/data/schemas/options-schema.json index 8cf0040087..24f3a6b0e4 100644 --- a/ext/data/schemas/options-schema.json +++ b/ext/data/schemas/options-schema.json @@ -823,7 +823,8 @@ "enabled", "allowSecondarySearches", "definitionsCollapsible", - "partsOfSpeechFilter" + "partsOfSpeechFilter", + "useDeinflections" ], "properties": { "name": { @@ -850,6 +851,10 @@ "partsOfSpeechFilter": { "type": "boolean", "default": true + }, + "useDeinflections": { + "type": "boolean", + "default": true } } } diff --git a/ext/data/templates/anki-field-templates-upgrade-v24.handlebars b/ext/data/templates/anki-field-templates-upgrade-v24.handlebars new file mode 100644 index 0000000000..2288737cc2 --- /dev/null +++ b/ext/data/templates/anki-field-templates-upgrade-v24.handlebars @@ -0,0 +1,52 @@ +{{#*inline "phonetic-transcriptions"}} + {{~#if (op ">" definition.phoneticTranscriptions.length 0)~}} + + {{~/if~}} +{{/inline}} + +{{<<<<<<<}} +{{#*inline "conjugation"}} + {{~#if (op ">" definition.inflectionRuleChainCandidates.length 0)~}} + {{~set "multiple" false~}} + {{~#if (op ">" definition.inflectionRuleChainCandidates.length 1)~}} + {{~set "multiple" true~}} + {{~/if~}} + {{~#if (get "multiple")~}}{{/if~}} + {{~/if~}} +{{/inline}} +{{=======}} +{{#*inline "conjugation"}} + {{~#if definition.reasons~}} + {{~#each definition.reasons~}} + {{~#if (op ">" @index 0)}} « {{/if~}} + {{.}} + {{~/each~}} + {{~/if~}} +{{/inline}} +{{>>>>>>>>}} diff --git a/ext/data/templates/default-anki-field-templates.handlebars b/ext/data/templates/default-anki-field-templates.handlebars index f23b9d0be9..818677cedf 100644 --- a/ext/data/templates/default-anki-field-templates.handlebars +++ b/ext/data/templates/default-anki-field-templates.handlebars @@ -261,11 +261,23 @@ {{/inline}} {{#*inline "conjugation"}} - {{~#if definition.reasons~}} - {{~#each definition.reasons~}} - {{~#if (op ">" @index 0)}} « {{/if~}} - {{.}} - {{~/each~}} + {{~#if (op ">" definition.inflectionRuleChainCandidates.length 0)~}} + {{~set "multiple" false~}} + {{~#if (op ">" definition.inflectionRuleChainCandidates.length 1)~}} + {{~set "multiple" true~}} + {{~/if~}} + {{~#if (get "multiple")~}}{{/if~}} {{~/if~}} {{/inline}} diff --git a/ext/display-templates.html b/ext/display-templates.html index ed0037bb6d..a50cea3b9d 100644 --- a/ext/display-templates.html +++ b/ext/display-templates.html @@ -32,7 +32,7 @@
-
+
@@ -77,6 +77,7 @@ + diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index a5a422728c..bc4f222f03 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -2428,7 +2428,8 @@ export class Backend { index: enabledDictionaryMap.size, priority: 0, allowSecondarySearches: false, - partsOfSpeechFilter: true + partsOfSpeechFilter: true, + useDeinflections: true }); excludeDictionaryDefinitions = new Set(); excludeDictionaryDefinitions.add(mainDictionary); @@ -2474,12 +2475,13 @@ export class Backend { const enabledDictionaryMap = new Map(); for (const dictionary of options.dictionaries) { if (!dictionary.enabled) { continue; } - const {name, priority, allowSecondarySearches, partsOfSpeechFilter} = dictionary; + const {name, priority, allowSecondarySearches, partsOfSpeechFilter, useDeinflections} = dictionary; enabledDictionaryMap.set(name, { index: enabledDictionaryMap.size, priority, allowSecondarySearches, - partsOfSpeechFilter + partsOfSpeechFilter, + useDeinflections }); } return enabledDictionaryMap; diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index c93e261d0f..0aabed6f73 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -556,7 +556,8 @@ export class OptionsUtil { this._updateVersion20, this._updateVersion21, this._updateVersion22, - this._updateVersion23 + this._updateVersion23, + this._updateVersion24 ]; if (typeof targetVersion === 'number' && targetVersion < result.length) { result.splice(targetVersion); @@ -1155,6 +1156,20 @@ export class OptionsUtil { } } + /** + * - Added dictionaries[].useDeinflections. + * @type {import('options-util').UpdateFunction} + */ + async _updateVersion24(options) { + await this._applyAnkiFieldTemplatesPatch(options, '/data/templates/anki-field-templates-upgrade-v24.handlebars'); + + for (const {options: profileOptions} of options.profiles) { + for (const dictionary of profileOptions.dictionaries) { + dictionary.useDeinflections = true; + } + } + } + /** * @param {string} url * @returns {Promise} diff --git a/ext/js/data/sandbox/anki-note-data-creator.js b/ext/js/data/sandbox/anki-note-data-creator.js index c0a1186980..77d6e357bf 100644 --- a/ext/js/data/sandbox/anki-note-data-creator.js +++ b/ext/js/data/sandbox/anki-note-data-creator.js @@ -376,7 +376,7 @@ export class AnkiNoteDataCreator { case 'merge': type = 'termMerged'; break; } - const {inflections, score, dictionaryIndex, dictionaryPriority, sourceTermExactMatchCount, definitions} = dictionaryEntry; + const {inflectionRuleChainCandidates, score, dictionaryIndex, dictionaryPriority, sourceTermExactMatchCount, definitions} = dictionaryEntry; let {url} = context; if (typeof url !== 'string') { url = ''; } @@ -401,7 +401,7 @@ export class AnkiNoteDataCreator { source: (primarySource !== null ? primarySource.transformedText : null), rawSource: (primarySource !== null ? primarySource.originalText : null), sourceTerm: (type !== 'termMerged' ? (primarySource !== null ? primarySource.deinflectedText : null) : void 0), - reasons: inflections, + inflectionRuleChainCandidates, score, isPrimary: (type === 'term' ? dictionaryEntry.isPrimary : void 0), get sequence() { return self.getCachedValue(sequence); }, diff --git a/ext/js/dictionary/dictionary-importer.js b/ext/js/dictionary/dictionary-importer.js index bfd7a8b2eb..8df7860ec7 100644 --- a/ext/js/dictionary/dictionary-importer.js +++ b/ext/js/dictionary/dictionary-importer.js @@ -160,7 +160,7 @@ export class DictionaryImporter { const glossaryList = entry.glossary; for (let j = 0, jj = glossaryList.length; j < jj; ++j) { const glossary = glossaryList[j]; - if (typeof glossary !== 'object' || glossary === null) { continue; } + if (typeof glossary !== 'object' || glossary === null || Array.isArray(glossary)) { continue; } glossaryList[j] = this._formatDictionaryTermGlossaryObject(glossary, entry, requirements); } if ((i % formatProgressInterval) === 0) { diff --git a/ext/js/display/display-generator.js b/ext/js/display/display-generator.js index 3a2a562115..521cbb4121 100644 --- a/ext/js/display/display-generator.js +++ b/ext/js/display/display-generator.js @@ -67,13 +67,13 @@ export class DisplayGenerator { const node = this._instantiate('term-entry'); const headwordsContainer = this._querySelector(node, '.headword-list'); - const inflectionsContainer = this._querySelector(node, '.inflection-list'); + const inflectionRuleChainsContainer = this._querySelector(node, '.inflection-rule-chains'); const groupedPronunciationsContainer = this._querySelector(node, '.pronunciation-group-list'); const frequencyGroupListContainer = this._querySelector(node, '.frequency-group-list'); const definitionsContainer = this._querySelector(node, '.definition-list'); const headwordTagsContainer = this._querySelector(node, '.headword-list-tag-list'); - const {headwords, type, inflections, definitions, frequencies, pronunciations} = dictionaryEntry; + const {headwords, type, inflectionRuleChainCandidates, definitions, frequencies, pronunciations} = dictionaryEntry; const groupedPronunciations = DictionaryDataUtil.getGroupedPronunciations(dictionaryEntry); const pronunciationCount = groupedPronunciations.reduce((i, v) => i + v.pronunciations.length, 0); const groupedFrequencies = DictionaryDataUtil.groupTermFrequencies(dictionaryEntry); @@ -112,7 +112,7 @@ export class DisplayGenerator { } headwordsContainer.dataset.count = `${headwords.length}`; - this._appendMultiple(inflectionsContainer, this._createTermInflection.bind(this), inflections); + this._appendMultiple(inflectionRuleChainsContainer, this._createInflectionRuleChain.bind(this), inflectionRuleChainCandidates); this._appendMultiple(frequencyGroupListContainer, this._createFrequencyGroup.bind(this), groupedFrequencies, false); this._appendMultiple(groupedPronunciationsContainer, this._createGroupedPronunciation.bind(this), groupedPronunciations); this._appendMultiple(headwordTagsContainer, this._createTermTag.bind(this), termTags, headwords.length); @@ -356,6 +356,44 @@ export class DisplayGenerator { return node; } + /** + * @param {import('dictionary').InflectionRuleChainCandidate} inflectionRuleChain + * @returns {?HTMLElement} + */ + _createInflectionRuleChain(inflectionRuleChain) { + const {source, inflectionRules} = inflectionRuleChain; + if (!Array.isArray(inflectionRules) || inflectionRules.length === 0) { return null; } + const fragment = this._instantiate('inflection-rule-chain'); + + const sourceIcon = this._getInflectionSourceIcon(source); + + fragment.appendChild(sourceIcon); + + this._appendMultiple(fragment, this._createTermInflection.bind(this), inflectionRules); + return fragment; + } + + /** + * @param {import('dictionary').InflectionSource} source + * @returns {HTMLElement} + */ + _getInflectionSourceIcon(source) { + const icon = document.createElement('span'); + icon.classList.add('inflection-source-icon'); + icon.dataset.inflectionSource = source; + switch (source) { + case 'dictionary': + icon.title = 'Dictionary Deinflection'; + return icon; + case 'algorithm': + icon.title = 'Algorithm Deinflection'; + return icon; + case 'both': + icon.title = 'Dictionary and Algorithm Deinflection'; + return icon; + } + } + /** * @param {string} inflection * @returns {DocumentFragment} @@ -396,7 +434,7 @@ export class DisplayGenerator { } /** - * @param {import('dictionary-data').TermGlossary} entry + * @param {import('dictionary-data').TermGlossaryContent} entry * @param {string} dictionary * @returns {?HTMLElement} */ diff --git a/ext/js/language/deinflector.js b/ext/js/language/deinflector.js index d2d92e53d6..e2b66cb40a 100644 --- a/ext/js/language/deinflector.js +++ b/ext/js/language/deinflector.js @@ -80,7 +80,7 @@ export class Deinflector { /** * @param {string} term * @param {import('translation-internal').DeinflectionRuleFlags} rules - * @param {string[]} reasons + * @param {import('dictionary').InflectionRuleChain} reasons * @returns {import('translation-internal').Deinflection} */ _createDeinflection(term, rules, reasons) { diff --git a/ext/js/language/translator.js b/ext/js/language/translator.js index 5441294b06..89a3e5ec67 100644 --- a/ext/js/language/translator.js +++ b/ext/js/language/translator.js @@ -70,7 +70,7 @@ export class Translator { async findTerms(mode, text, options) { const {enabledDictionaryMap, excludeDictionaryDefinitions, sortFrequencyDictionary, sortFrequencyDictionaryOrder} = options; const tagAggregator = new TranslatorTagAggregator(); - let {dictionaryEntries, originalTextLength} = await this._findTermsInternalWrapper(text, enabledDictionaryMap, options, tagAggregator); + let {dictionaryEntries, originalTextLength} = await this._findTermsInternal(text, enabledDictionaryMap, options, tagAggregator); switch (mode) { case 'group': @@ -208,7 +208,7 @@ export class Translator { * @param {TranslatorTagAggregator} tagAggregator * @returns {Promise} */ - async _findTermsInternalWrapper(text, enabledDictionaryMap, options, tagAggregator) { + async _findTermsInternal(text, enabledDictionaryMap, options, tagAggregator) { if (options.removeNonJapaneseCharacters) { text = this._getJapaneseOnlyText(text); } @@ -216,18 +216,30 @@ export class Translator { return {dictionaryEntries: [], originalTextLength: 0}; } - const deinflections = await this._findTermsInternal(text, enabledDictionaryMap, options); + const deinflections = await this._getDeinflections(text, enabledDictionaryMap, options); let originalTextLength = 0; + /** @type {import('dictionary').TermDictionaryEntry[]} */ const dictionaryEntries = []; const ids = new Set(); - for (const {databaseEntries, originalText, transformedText, deinflectedText, reasons} of deinflections) { + for (const {databaseEntries, originalText, transformedText, deinflectedText, inflectionRuleChainCandidates} of deinflections) { if (databaseEntries.length === 0) { continue; } originalTextLength = Math.max(originalTextLength, originalText.length); for (const databaseEntry of databaseEntries) { const {id} = databaseEntry; - if (ids.has(id)) { continue; } - const dictionaryEntry = this._createTermDictionaryEntryFromDatabaseEntry(databaseEntry, originalText, transformedText, deinflectedText, reasons, true, enabledDictionaryMap, tagAggregator); + if (ids.has(id)) { + const existingEntry = dictionaryEntries.find((entry) => { + return entry.definitions.some((definition) => definition.id === id); + }); + + if (existingEntry && transformedText.length >= existingEntry.headwords[0].sources[0].transformedText.length) { + this._mergeInflectionRuleChains(existingEntry, inflectionRuleChainCandidates); + } + + continue; + } + + const dictionaryEntry = this._createTermDictionaryEntryFromDatabaseEntry(databaseEntry, originalText, transformedText, deinflectedText, inflectionRuleChainCandidates, true, enabledDictionaryMap, tagAggregator); dictionaryEntries.push(dictionaryEntry); ids.add(id); } @@ -236,41 +248,164 @@ export class Translator { return {dictionaryEntries, originalTextLength}; } + /** + * @param {import('dictionary').TermDictionaryEntry} existingEntry + * @param {import('dictionary').InflectionRuleChainCandidate[]} inflectionRuleChainCandidates + */ + _mergeInflectionRuleChains(existingEntry, inflectionRuleChainCandidates) { + const existingChains = existingEntry.inflectionRuleChainCandidates; + + for (const {source, inflectionRules} of inflectionRuleChainCandidates) { + const duplicate = existingChains.find((existingChain) => this._areArraysEqualIgnoreOrder(existingChain.inflectionRules, inflectionRules)); + if (!duplicate) { + existingEntry.inflectionRuleChainCandidates.push({source, inflectionRules}); + } else if (duplicate.source !== source) { + duplicate.source = 'both'; + } + } + } + + /** + * @param {string[]} array1 + * @param {string[]} array2 + * @returns {boolean} + */ + _areArraysEqualIgnoreOrder(array1, array2) { + if (array1.length !== array2.length) { + return false; + } + + const frequencyCounter = new Map(); + + for (const element of array1) { + frequencyCounter.set(element, (frequencyCounter.get(element) || 0) + 1); + } + + for (const element of array2) { + const frequency = frequencyCounter.get(element); + if (!frequency) { + return false; + } + frequencyCounter.set(element, frequency - 1); + } + + return true; + } + + /** * @param {string} text * @param {Map} enabledDictionaryMap * @param {import('translation').FindTermsOptions} options * @returns {Promise} */ - async _findTermsInternal(text, enabledDictionaryMap, options) { - const deinflections = ( + async _getDeinflections(text, enabledDictionaryMap, options) { + let deinflections = ( options.deinflect ? - this._getAllDeinflections(text, options) : + this._getAlgorithmDeinflections(text, options) : [this._createDeinflection(text, text, text, 0, [])] ); if (deinflections.length === 0) { return []; } - const uniqueDeinflectionTerms = []; - const uniqueDeinflectionArrays = []; - const uniqueDeinflectionsMap = new Map(); + const {matchType} = options; + + await this._addEntriesToDeinflections(deinflections, enabledDictionaryMap, matchType); + + const dictionaryDeinflections = await this._getDictionaryDeinflections(deinflections, enabledDictionaryMap, matchType); + deinflections.push(...dictionaryDeinflections); + for (const deinflection of deinflections) { - const term = deinflection.deinflectedText; - let deinflectionArray = uniqueDeinflectionsMap.get(term); + for (const entry of deinflection.databaseEntries) { + entry.definitions = entry.definitions.filter((definition) => !Array.isArray(definition)); + } + deinflection.databaseEntries = deinflection.databaseEntries.filter((entry) => entry.definitions.length); + } + deinflections = deinflections.filter((deinflection) => deinflection.databaseEntries.length); + + return deinflections; + } + + /** + * @param {import('translation-internal').DatabaseDeinflection[]} deinflections + * @param {Map} enabledDictionaryMap + * @param {import('dictionary').TermSourceMatchType} matchType + * @returns {Promise} + */ + async _getDictionaryDeinflections(deinflections, enabledDictionaryMap, matchType) { + /** @type {import('translation-internal').DatabaseDeinflection[]} */ + const dictionaryDeinflections = []; + for (const deinflection of deinflections) { + const {originalText, transformedText, inflectionRuleChainCandidates: algorithmChains, databaseEntries} = deinflection; + for (const entry of databaseEntries) { + const {dictionary, definitions} = entry; + const entryDictionary = enabledDictionaryMap.get(dictionary); + const useDeinflections = entryDictionary?.useDeinflections ?? true; + if (!useDeinflections) { continue; } + for (const definition of definitions) { + if (Array.isArray(definition)) { + const [formOf, inflectionRules] = definition; + if (!formOf) { continue; } + + const inflectionRuleChainCandidates = algorithmChains.map(({inflectionRules: algInflections}) => { + return { + source: /** @type {import('dictionary').InflectionSource} */ (algInflections.length === 0 ? 'dictionary' : 'both'), + inflectionRules: [...algInflections, ...inflectionRules] + }; + }); + + const dictionaryDeinflection = this._createDeinflection(originalText, transformedText, formOf, 0, inflectionRuleChainCandidates); + dictionaryDeinflections.push(dictionaryDeinflection); + } + } + } + } + + await this._addEntriesToDeinflections(dictionaryDeinflections, enabledDictionaryMap, matchType); + + return dictionaryDeinflections; + } + + /** + * @param {import('translation-internal').DatabaseDeinflection[]} deinflections + * @param {Map} enabledDictionaryMap + * @param {import('dictionary').TermSourceMatchType} matchType + */ + async _addEntriesToDeinflections(deinflections, enabledDictionaryMap, matchType) { + const uniqueDeinflectionsMap = this._groupDeinflectionsByTerm(deinflections); + const uniqueDeinflectionArrays = [...uniqueDeinflectionsMap.values()]; + const uniqueDeinflectionTerms = [...uniqueDeinflectionsMap.keys()]; + + const databaseEntries = await this._database.findTermsBulk(uniqueDeinflectionTerms, enabledDictionaryMap, matchType); + this._matchEntriesToDeinflections(databaseEntries, uniqueDeinflectionArrays, enabledDictionaryMap); + } + + /** + * @param {import('translation-internal').DatabaseDeinflection[]} deinflections + * @returns {Map} + */ + _groupDeinflectionsByTerm(deinflections) { + const result = new Map(); + for (const deinflection of deinflections) { + const {deinflectedText} = deinflection; + let deinflectionArray = result.get(deinflectedText); if (typeof deinflectionArray === 'undefined') { deinflectionArray = []; - uniqueDeinflectionTerms.push(term); - uniqueDeinflectionArrays.push(deinflectionArray); - uniqueDeinflectionsMap.set(term, deinflectionArray); + result.set(deinflectedText, deinflectionArray); } deinflectionArray.push(deinflection); } + return result; + } - const {matchType} = options; - const databaseEntries = await this._database.findTermsBulk(uniqueDeinflectionTerms, enabledDictionaryMap, matchType); - + /** + * @param {import('dictionary-database').TermEntry[]} databaseEntries + * @param {import('translation-internal').DatabaseDeinflection[][]} uniqueDeinflectionArrays + * @param {Map} enabledDictionaryMap + */ + _matchEntriesToDeinflections(databaseEntries, uniqueDeinflectionArrays, enabledDictionaryMap) { for (const databaseEntry of databaseEntries) { const entryDictionary = /** @type {import('translation').FindTermDictionary} */ (enabledDictionaryMap.get(databaseEntry.dictionary)); - const partsOfSpeechFilter = entryDictionary.partsOfSpeechFilter; + const {partsOfSpeechFilter} = entryDictionary; const definitionRules = Deinflector.rulesToRuleFlags(databaseEntry.rules); for (const deinflection of uniqueDeinflectionArrays[databaseEntry.index]) { @@ -280,8 +415,6 @@ export class Translator { } } } - - return deinflections; } // Deinflections and text transformations @@ -291,7 +424,7 @@ export class Translator { * @param {import('translation').FindTermsOptions} options * @returns {import('translation-internal').DatabaseDeinflection[]} */ - _getAllDeinflections(text, options) { + _getAlgorithmDeinflections(text, options) { /** @type {import('translation-internal').TextDeinflectionOptionsArrays} */ const textOptionVariantArray = [ this._getTextReplacementsVariants(options), @@ -342,7 +475,12 @@ export class Translator { used.add(source); const rawSource = sourceMap.source.substring(0, sourceMap.getSourceLength(i)); for (const {term, rules, reasons} of /** @type {Deinflector} */ (this._deinflector).deinflect(source)) { - deinflections.push(this._createDeinflection(rawSource, source, term, rules, reasons)); + /** @type {import('dictionary').InflectionRuleChainCandidate} */ + const inflectionRuleChainCandidate = { + source: 'algorithm', + inflectionRules: reasons + }; + deinflections.push(this._createDeinflection(rawSource, source, term, rules, [inflectionRuleChainCandidate])); } } } @@ -435,11 +573,11 @@ export class Translator { * @param {string} transformedText * @param {string} deinflectedText * @param {import('translation-internal').DeinflectionRuleFlags} rules - * @param {string[]} reasons + * @param {import('dictionary').InflectionRuleChainCandidate[]} inflectionRuleChainCandidates * @returns {import('translation-internal').DatabaseDeinflection} */ - _createDeinflection(originalText, transformedText, deinflectedText, rules, reasons) { - return {originalText, transformedText, deinflectedText, rules, reasons, databaseEntries: []}; + _createDeinflection(originalText, transformedText, deinflectedText, rules, inflectionRuleChainCandidates) { + return {originalText, transformedText, deinflectedText, rules, inflectionRuleChainCandidates, databaseEntries: []}; } // Term dictionary entry grouping @@ -597,8 +735,8 @@ export class Translator { _groupDictionaryEntriesByHeadword(dictionaryEntries, tagAggregator) { const groups = new Map(); for (const dictionaryEntry of dictionaryEntries) { - const {inflections, headwords: [{term, reading}]} = dictionaryEntry; - const key = this._createMapKey([term, reading, ...inflections]); + const {inflectionRuleChainCandidates, headwords: [{term, reading}]} = dictionaryEntry; + const key = this._createMapKey([term, reading, ...inflectionRuleChainCandidates]); let groupDictionaryEntries = groups.get(key); if (typeof groupDictionaryEntries === 'undefined') { groupDictionaryEntries = []; @@ -1370,7 +1508,7 @@ export class Translator { * @param {number[]} sequences * @param {boolean} isPrimary * @param {import('dictionary').Tag[]} tags - * @param {import('dictionary-data').TermGlossary[]} entries + * @param {import('dictionary-data').TermGlossaryContent[]} entries * @returns {import('dictionary').TermDefinition} */ _createTermDefinition(index, headwordIndices, dictionary, dictionaryIndex, dictionaryPriority, id, score, sequences, isPrimary, tags, entries) { @@ -1421,7 +1559,7 @@ export class Translator { /** * @param {boolean} isPrimary - * @param {string[]} inflections + * @param {import('dictionary').InflectionRuleChainCandidate[]} inflectionRuleChainCandidates * @param {number} score * @param {number} dictionaryIndex * @param {number} dictionaryPriority @@ -1431,11 +1569,11 @@ export class Translator { * @param {import('dictionary').TermDefinition[]} definitions * @returns {import('dictionary').TermDictionaryEntry} */ - _createTermDictionaryEntry(isPrimary, inflections, score, dictionaryIndex, dictionaryPriority, sourceTermExactMatchCount, maxTransformedTextLength, headwords, definitions) { + _createTermDictionaryEntry(isPrimary, inflectionRuleChainCandidates, score, dictionaryIndex, dictionaryPriority, sourceTermExactMatchCount, maxTransformedTextLength, headwords, definitions) { return { type: 'term', isPrimary, - inflections, + inflectionRuleChainCandidates, score, frequencyOrder: 0, dictionaryIndex, @@ -1454,14 +1592,29 @@ export class Translator { * @param {string} originalText * @param {string} transformedText * @param {string} deinflectedText - * @param {string[]} reasons + * @param {import('dictionary').InflectionRuleChainCandidate[]} inflectionRuleChainCandidates * @param {boolean} isPrimary * @param {Map} enabledDictionaryMap * @param {TranslatorTagAggregator} tagAggregator * @returns {import('dictionary').TermDictionaryEntry} */ - _createTermDictionaryEntryFromDatabaseEntry(databaseEntry, originalText, transformedText, deinflectedText, reasons, isPrimary, enabledDictionaryMap, tagAggregator) { - const {matchType, matchSource, term, reading: rawReading, definitionTags, termTags, definitions, score, dictionary, id, sequence: rawSequence, rules} = databaseEntry; + _createTermDictionaryEntryFromDatabaseEntry(databaseEntry, originalText, transformedText, deinflectedText, inflectionRuleChainCandidates, isPrimary, enabledDictionaryMap, tagAggregator) { + const { + matchType, + matchSource, + term, + reading: rawReading, + definitionTags, + termTags, + definitions, + score, + dictionary, + id, + sequence: rawSequence, + rules + } = databaseEntry; + // cast is safe because getDeinflections filters out deinflection definitions + const contentDefinitions = /** @type {import('dictionary-data').TermGlossaryContent[]} */ (definitions); const reading = (rawReading.length > 0 ? rawReading : term); const {index: dictionaryIndex, priority: dictionaryPriority} = this._getDictionaryOrder(dictionary, enabledDictionaryMap); const sourceTermExactMatchCount = (isPrimary && deinflectedText === term ? 1 : 0); @@ -1479,14 +1632,14 @@ export class Translator { return this._createTermDictionaryEntry( isPrimary, - reasons, + inflectionRuleChainCandidates, score, dictionaryIndex, dictionaryPriority, sourceTermExactMatchCount, maxTransformedTextLength, [this._createTermHeadword(0, term, reading, [source], headwordTagGroups, rules)], - [this._createTermDefinition(0, [0], dictionary, dictionaryIndex, dictionaryPriority, id, score, [sequence], isPrimary, definitionTagGroups, definitions)] + [this._createTermDefinition(0, [0], dictionary, dictionaryIndex, dictionaryPriority, id, score, [sequence], isPrimary, definitionTagGroups, contentDefinitions)] ); } @@ -1530,7 +1683,7 @@ export class Translator { if (dictionaryEntry.isPrimary) { isPrimary = true; maxTransformedTextLength = Math.max(maxTransformedTextLength, dictionaryEntry.maxTransformedTextLength); - const dictionaryEntryInflections = dictionaryEntry.inflections; + const dictionaryEntryInflections = dictionaryEntry.inflectionRuleChainCandidates; if (inflections === null || dictionaryEntryInflections.length < inflections.length) { inflections = dictionaryEntryInflections; } @@ -1742,7 +1895,7 @@ export class Translator { if (i !== 0) { return i; } // Sort by the number of inflection reasons - i = v1.inflections.length - v2.inflections.length; + i = v1.inflectionRuleChainCandidates.length - v2.inflectionRuleChainCandidates.length; if (i !== 0) { return i; } // Sort by how many terms exactly match the source (e.g. for exact kana prioritization) diff --git a/ext/js/pages/settings/dictionary-controller.js b/ext/js/pages/settings/dictionary-controller.js index 18a802be1e..1d3da532c5 100644 --- a/ext/js/pages/settings/dictionary-controller.js +++ b/ext/js/pages/settings/dictionary-controller.js @@ -185,6 +185,10 @@ class DictionaryEntry { const partsOfSpeechFilterSetting = querySelectorNotNull(modal.node, '.dictionary-parts-of-speech-filter-setting'); /** @type {HTMLElement} */ const partsOfSpeechFilterToggle = querySelectorNotNull(partsOfSpeechFilterSetting, '.dictionary-parts-of-speech-filter-toggle'); + /** @type {HTMLElement} */ + const useDeinflectionsSetting = querySelectorNotNull(modal.node, '.dictionary-use-deinflections-setting'); + /** @type {HTMLElement} */ + const useDeinflectionsToggle = querySelectorNotNull(useDeinflectionsSetting, '.dictionary-use-deinflections-toggle'); titleElement.textContent = title; versionElement.textContent = `rev.${revision}`; @@ -194,6 +198,9 @@ class DictionaryEntry { partsOfSpeechFilterSetting.hidden = !counts.terms.total; partsOfSpeechFilterToggle.dataset.setting = `dictionaries[${this._index}].partsOfSpeechFilter`; + useDeinflectionsSetting.hidden = !counts.terms.total; + useDeinflectionsToggle.dataset.setting = `dictionaries[${this._index}].useDeinflections`; + this._setupDetails(detailsTableElement); modal.setVisible(true); @@ -521,7 +528,8 @@ export class DictionaryController { enabled, allowSecondarySearches: false, definitionsCollapsible: 'not-collapsible', - partsOfSpeechFilter: true + partsOfSpeechFilter: true, + useDeinflections: true }; } diff --git a/ext/js/templates/sandbox/anki-template-renderer.js b/ext/js/templates/sandbox/anki-template-renderer.js index 3311097f2e..e4822bee65 100644 --- a/ext/js/templates/sandbox/anki-template-renderer.js +++ b/ext/js/templates/sandbox/anki-template-renderer.js @@ -675,7 +675,7 @@ export class AnkiTemplateRenderer { * @type {import('template-renderer').HelperFunction} */ _formatGlossary(args, _context, options) { - const [dictionary, content] = /** @type {[dictionary: string, content: import('dictionary-data').TermGlossary]} */ (args); + const [dictionary, content] = /** @type {[dictionary: string, content: import('dictionary-data').TermGlossaryContent]} */ (args); const data = options.data.root; if (typeof content === 'string') { return this._stringToMultiLineHtml(this._escape(content)); } if (!(typeof content === 'object' && content !== null)) { return ''; } diff --git a/ext/settings.html b/ext/settings.html index 2cc521d5eb..3a4c90ce20 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -2691,6 +2691,24 @@

Yomitan Settings


+ +
diff --git a/test/data/anki-note-builder-test-results.json b/test/data/anki-note-builder-test-results.json index 86bffc6a0e..7129d74803 100644 --- a/test/data/anki-note-builder-test-results.json +++ b/test/data/anki-note-builder-test-results.json @@ -2867,5 +2867,41 @@ "url": "url:" } ] + }, + { + "name": "Test dictionary deinflection", + "results": [ + { + "audio": "", + "clipboard-image": "", + "clipboard-text": "", + "cloze-body": "のたもうた", + "cloze-prefix": "cloze-prefix", + "cloze-suffix": "cloze-suffix", + "conjugation": "past", + "dictionary": "Test Dictionary 2", + "document-title": "title", + "expression": "のたまう", + "frequencies": "", + "furigana": "のたまう", + "furigana-plain": "のたまう", + "glossary": "
(v5, Test Dictionary 2) notamau definition
", + "glossary-brief": "
notamau definition
", + "glossary-no-dictionary": "
(v5) notamau definition
", + "part-of-speech": "Godan verb", + "pitch-accents": "No pitch accent data", + "pitch-accent-graphs": "No pitch accent data", + "pitch-accent-positions": "No pitch accent data", + "phonetic-transcriptions": "", + "reading": "のたまう", + "screenshot": "", + "search-query": "fullQuery", + "selection-text": "", + "sentence": "cloze-prefixのたもうたcloze-suffix", + "sentence-furigana": "cloze-prefixのたもうたcloze-suffix", + "tags": "v5", + "url": "url:" + } + ] } ] \ No newline at end of file diff --git a/test/data/database-test-cases.json b/test/data/database-test-cases.json index 02fddd4931..611903dd85 100644 --- a/test/data/database-test-cases.json +++ b/test/data/database-test-cases.json @@ -27,7 +27,7 @@ "ipa": 1 }, "terms": { - "total": 23 + "total": 25 } } }, @@ -36,7 +36,7 @@ { "kanji": 2, "kanjiMeta": 6, - "terms": 23, + "terms": 25, "termMeta": 39, "tagMeta": 15, "media": 6 @@ -45,7 +45,7 @@ "total": { "kanji": 2, "kanjiMeta": 6, - "terms": 23, + "terms": 25, "termMeta": 39, "tagMeta": 15, "media": 6 diff --git a/test/data/dictionaries/valid-dictionary1/term_bank_1.json b/test/data/dictionaries/valid-dictionary1/term_bank_1.json index ce4290bdff..7f2af6ddc9 100644 --- a/test/data/dictionaries/valid-dictionary1/term_bank_1.json +++ b/test/data/dictionaries/valid-dictionary1/term_bank_1.json @@ -337,5 +337,7 @@ {"type": "structured-content", "content": "kouzou definition 3 (構造)"} ], 101, "P E1" - ] + ], + ["のたまう", "のたまう", "v5", "v5", 1, ["notamau definition"], 15, ""], + ["のたもうた", "のたもうた", "", "", 1, [["のたまう", ["past"]]], 16, ""] ] \ No newline at end of file diff --git a/test/data/translator-test-inputs.json b/test/data/translator-test-inputs.json index 91cf0ab2bb..ce449c1e03 100644 --- a/test/data/translator-test-inputs.json +++ b/test/data/translator-test-inputs.json @@ -35,7 +35,8 @@ "index": 0, "priority": 0, "allowSecondarySearches": false, - "partsOfSpeechFilter": true + "partsOfSpeechFilter": true, + "useDeinflections": true } ] ] @@ -344,6 +345,13 @@ "mode": "split", "text": "構造", "options": "default" + }, + { + "name": "Test dictionary deinflection", + "func": "findTerms", + "mode": "split", + "text": "のたもうた", + "options": "default" } ] } \ No newline at end of file diff --git a/test/data/translator-test-results-note-data1.json b/test/data/translator-test-results-note-data1.json index 1342a63fed..c34842da82 100644 --- a/test/data/translator-test-results-note-data1.json +++ b/test/data/translator-test-results-note-data1.json @@ -341,7 +341,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 1, @@ -647,7 +652,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 2, @@ -967,7 +977,12 @@ "source": "打つ", "rawSource": "打つ", "sourceTerm": "打つ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 3, @@ -1273,7 +1288,12 @@ "source": "打つ", "rawSource": "打つ", "sourceTerm": "打つ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 3, @@ -1579,7 +1599,12 @@ "source": "打つ", "rawSource": "打つ", "sourceTerm": "打つ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 3, @@ -1885,7 +1910,12 @@ "source": "打つ", "rawSource": "打つ", "sourceTerm": "打つ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 3, @@ -2191,7 +2221,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 1, @@ -2497,7 +2532,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 2, @@ -2817,7 +2857,12 @@ "source": "打ち込む", "rawSource": "打ち込む", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 4, @@ -3231,7 +3276,12 @@ "source": "打ち込む", "rawSource": "打ち込む", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 4, @@ -3645,7 +3695,12 @@ "source": "打ち込む", "rawSource": "打ち込む", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 4, @@ -4059,7 +4114,12 @@ "source": "打ち込む", "rawSource": "打ち込む", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 4, @@ -4473,8 +4533,13 @@ "source": "打ち", "rawSource": "打ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "isPrimary": true, @@ -4781,8 +4846,13 @@ "source": "打ち", "rawSource": "打ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "isPrimary": true, @@ -5089,8 +5159,13 @@ "source": "打ち", "rawSource": "打ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "isPrimary": true, @@ -5397,8 +5472,13 @@ "source": "打ち", "rawSource": "打ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "isPrimary": true, @@ -5705,7 +5785,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 1, @@ -6011,7 +6096,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 2, @@ -6331,7 +6421,12 @@ "source": "画像", "rawSource": "画像", "sourceTerm": "画像", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 5, @@ -6485,7 +6580,12 @@ "source": "だ", "rawSource": "だ", "sourceTerm": "だ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 1, @@ -6796,7 +6896,12 @@ "source": "ダース", "rawSource": "ダース", "sourceTerm": "ダース", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 2, @@ -7116,7 +7221,12 @@ "source": "うつ", "rawSource": "うつ", "sourceTerm": "うつ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 3, @@ -7422,7 +7532,12 @@ "source": "うつ", "rawSource": "うつ", "sourceTerm": "うつ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 3, @@ -7733,7 +7848,12 @@ "source": "ぶつ", "rawSource": "ぶつ", "sourceTerm": "ぶつ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 3, @@ -8039,7 +8159,12 @@ "source": "ぶつ", "rawSource": "ぶつ", "sourceTerm": "ぶつ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 3, @@ -8350,7 +8475,12 @@ "source": "うちこむ", "rawSource": "うちこむ", "sourceTerm": "うちこむ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 4, @@ -8764,7 +8894,12 @@ "source": "うちこむ", "rawSource": "うちこむ", "sourceTerm": "うちこむ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 4, @@ -9178,8 +9313,13 @@ "source": "うち", "rawSource": "うち", "sourceTerm": "うつ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "isPrimary": true, @@ -9486,8 +9626,13 @@ "source": "うち", "rawSource": "うち", "sourceTerm": "うつ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "isPrimary": true, @@ -9799,7 +9944,12 @@ "source": "ぶちこむ", "rawSource": "ぶちこむ", "sourceTerm": "ぶちこむ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 4, @@ -10213,7 +10363,12 @@ "source": "ぶちこむ", "rawSource": "ぶちこむ", "sourceTerm": "ぶちこむ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 4, @@ -10627,8 +10782,13 @@ "source": "ぶち", "rawSource": "ぶち", "sourceTerm": "ぶつ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "isPrimary": true, @@ -10935,8 +11095,13 @@ "source": "ぶち", "rawSource": "ぶち", "sourceTerm": "ぶつ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "isPrimary": true, @@ -11248,7 +11413,12 @@ "source": "がぞう", "rawSource": "がぞう", "sourceTerm": "がぞう", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 5, @@ -11413,7 +11583,12 @@ "source": "打ち込む", "rawSource": "打ち込む", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "sequence": 4, "dictionary": "Test Dictionary 2", @@ -11850,7 +12025,12 @@ "source": "打ち込む", "rawSource": "打ち込む", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "sequence": 4, "dictionary": "Test Dictionary 2", @@ -12287,8 +12467,13 @@ "source": "打ち", "rawSource": "打ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "sequence": 3, @@ -12626,8 +12811,13 @@ "source": "打ち", "rawSource": "打ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "sequence": 3, @@ -12965,7 +13155,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "sequence": 1, "dictionary": "Test Dictionary 2", @@ -13269,7 +13464,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "sequence": 2, "dictionary": "Test Dictionary 2", @@ -13586,7 +13786,12 @@ "type": "termMerged", "source": "打ち込む", "rawSource": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "sequence": 4, "dictionary": "Test Dictionary 2", @@ -14351,8 +14556,13 @@ "type": "termMerged", "source": "打ち", "rawSource": "打ち", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "sequence": 3, @@ -14923,7 +15133,12 @@ "type": "termMerged", "source": "打", "rawSource": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "sequence": 1, "dictionary": "Test Dictionary 2", @@ -15220,7 +15435,12 @@ "type": "termMerged", "source": "打", "rawSource": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "sequence": 2, "dictionary": "Test Dictionary 2", @@ -15533,10 +15753,15 @@ "source": "打ち込んでいませんでした", "rawSource": "打ち込んでいませんでした", "sourceTerm": "打ち込む", - "reasons": [ - "-te", - "progressive or perfect", - "polite past negative" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "-te", + "progressive or perfect", + "polite past negative" + ] + } ], "score": 10, "isPrimary": true, @@ -15951,10 +16176,15 @@ "source": "打ち込んでいませんでした", "rawSource": "打ち込んでいませんでした", "sourceTerm": "打ち込む", - "reasons": [ - "-te", - "progressive or perfect", - "polite past negative" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "-te", + "progressive or perfect", + "polite past negative" + ] + } ], "score": 10, "isPrimary": true, @@ -16369,10 +16599,15 @@ "source": "打ち込んでいませんでした", "rawSource": "打ち込んでいませんでした", "sourceTerm": "打ち込む", - "reasons": [ - "-te", - "progressive or perfect", - "polite past negative" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "-te", + "progressive or perfect", + "polite past negative" + ] + } ], "score": 1, "isPrimary": true, @@ -16787,10 +17022,15 @@ "source": "打ち込んでいませんでした", "rawSource": "打ち込んでいませんでした", "sourceTerm": "打ち込む", - "reasons": [ - "-te", - "progressive or perfect", - "polite past negative" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "-te", + "progressive or perfect", + "polite past negative" + ] + } ], "score": 1, "isPrimary": true, @@ -17205,8 +17445,13 @@ "source": "打ち", "rawSource": "打ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "isPrimary": true, @@ -17513,8 +17758,13 @@ "source": "打ち", "rawSource": "打ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "isPrimary": true, @@ -17821,8 +18071,13 @@ "source": "打ち", "rawSource": "打ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "isPrimary": true, @@ -18129,8 +18384,13 @@ "source": "打ち", "rawSource": "打ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "isPrimary": true, @@ -18437,7 +18697,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 1, @@ -18743,7 +19008,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 2, @@ -19063,7 +19333,12 @@ "source": "打ち込む", "rawSource": "打(う)ち込(こ)む", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 4, @@ -19477,7 +19752,12 @@ "source": "打ち込む", "rawSource": "打(う)ち込(こ)む", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 4, @@ -19891,7 +20171,12 @@ "source": "打ち込む", "rawSource": "打(う)ち込(こ)む", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 4, @@ -20305,7 +20590,12 @@ "source": "打ち込む", "rawSource": "打(う)ち込(こ)む", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 4, @@ -20719,8 +21009,13 @@ "source": "打ち", "rawSource": "打(う)ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "isPrimary": true, @@ -21027,8 +21322,13 @@ "source": "打ち", "rawSource": "打(う)ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "isPrimary": true, @@ -21335,8 +21635,13 @@ "source": "打ち", "rawSource": "打(う)ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "isPrimary": true, @@ -21643,8 +21948,13 @@ "source": "打ち", "rawSource": "打(う)ち", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "isPrimary": true, @@ -21951,7 +22261,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 1, @@ -22257,7 +22572,12 @@ "source": "打", "rawSource": "打", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 2, @@ -22577,7 +22897,12 @@ "source": "打ち込む", "rawSource": "(打)(ち)(込)(む)", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 4, @@ -22991,7 +23316,12 @@ "source": "打ち込む", "rawSource": "(打)(ち)(込)(む)", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "isPrimary": true, "sequence": 4, @@ -23405,7 +23735,12 @@ "source": "打ち込む", "rawSource": "(打)(ち)(込)(む)", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 4, @@ -23819,7 +24154,12 @@ "source": "打ち込む", "rawSource": "(打)(ち)(込)(む)", "sourceTerm": "打ち込む", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 4, @@ -24233,8 +24573,13 @@ "source": "打ち", "rawSource": "(打)(ち)", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "isPrimary": true, @@ -24541,8 +24886,13 @@ "source": "打ち", "rawSource": "(打)(ち)", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "isPrimary": true, @@ -24849,8 +25199,13 @@ "source": "打ち", "rawSource": "(打)(ち)", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "isPrimary": true, @@ -25157,8 +25512,13 @@ "source": "打ち", "rawSource": "(打)(ち)", "sourceTerm": "打つ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "isPrimary": true, @@ -25465,7 +25825,12 @@ "source": "打", "rawSource": "(打)", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 1, @@ -25771,7 +26136,12 @@ "source": "打", "rawSource": "(打)", "sourceTerm": "打", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 2, @@ -26091,8 +26461,13 @@ "source": "よみ", "rawSource": "test", "sourceTerm": "よむ", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 100, "isPrimary": true, @@ -26245,7 +26620,12 @@ "source": "つよみ", "rawSource": "つtest", "sourceTerm": "つよみ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 90, "isPrimary": true, "sequence": 7, @@ -26397,8 +26777,13 @@ "source": "よみました", "rawSource": "testました", "sourceTerm": "よむ", - "reasons": [ - "polite past" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "polite past" + ] + } ], "score": 100, "isPrimary": true, @@ -26549,7 +26934,12 @@ "type": "termMerged", "source": "うちこむ", "rawSource": "うちこむ", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "sequence": 4, "dictionary": "Test Dictionary 2", @@ -27314,8 +27704,13 @@ "type": "termMerged", "source": "うち", "rawSource": "うち", - "reasons": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "sequence": 3, @@ -27893,7 +28288,12 @@ "source": "お手前", "rawSource": "お手前", "sourceTerm": "お手前", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 9, @@ -28221,7 +28621,12 @@ "source": "番号", "rawSource": "番号", "sourceTerm": "番号", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 10, @@ -28401,7 +28806,12 @@ "source": "中腰", "rawSource": "中腰", "sourceTerm": "中腰", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 11, @@ -28581,7 +28991,12 @@ "source": "所業", "rawSource": "所業", "sourceTerm": "所業", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 12, @@ -28761,7 +29176,12 @@ "source": "土木工事", "rawSource": "土木工事", "sourceTerm": "土木工事", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 13, @@ -28941,7 +29361,12 @@ "source": "好き", "rawSource": "好き", "sourceTerm": "好き", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "isPrimary": true, "sequence": 14, @@ -29149,7 +29574,12 @@ "source": "構造", "rawSource": "構造", "sourceTerm": "構造", - "reasons": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 35, "isPrimary": true, "sequence": 101, @@ -29289,5 +29719,118 @@ "media": {} } ] + }, + { + "name": "Test dictionary deinflection", + "noteDataList": [ + { + "marker": "{marker}", + "definition": { + "type": "term", + "id": 23, + "source": "のたもうた", + "rawSource": "のたもうた", + "sourceTerm": "のたまう", + "inflectionRuleChainCandidates": [ + { + "source": "both", + "inflectionRules": [ + "past" + ] + } + ], + "score": 1, + "isPrimary": true, + "sequence": 15, + "dictionary": "Test Dictionary 2", + "dictionaryOrder": { + "index": 0, + "priority": 0 + }, + "dictionaryNames": [ + "Test Dictionary 2" + ], + "expression": "のたまう", + "reading": "のたまう", + "expressions": [ + { + "sourceTerm": "のたまう", + "expression": "のたまう", + "reading": "のたまう", + "termTags": [], + "frequencies": [], + "pitches": [], + "furiganaSegments": [ + { + "text": "のたまう", + "furigana": "" + } + ], + "termFrequency": "normal", + "wordClasses": [ + "v5" + ] + } + ], + "glossary": [ + "notamau definition" + ], + "definitionTags": [ + { + "name": "v5", + "category": "default", + "notes": "", + "order": 0, + "score": 0, + "dictionary": "Test Dictionary 2", + "redundant": false + } + ], + "termTags": [], + "frequencies": [], + "pitches": [], + "phoneticTranscriptions": [], + "sourceTermExactMatchCount": 1, + "url": "url:", + "cloze": { + "sentence": "", + "prefix": "", + "body": "", + "suffix": "" + }, + "furiganaSegments": [ + { + "text": "のたまう", + "furigana": "" + } + ] + }, + "glossaryLayoutMode": "default", + "compactTags": false, + "group": false, + "merge": false, + "modeTermKanji": false, + "modeTermKana": false, + "modeKanji": false, + "compactGlossaries": false, + "uniqueExpressions": [ + "のたまう" + ], + "uniqueReadings": [ + "のたまう" + ], + "pitches": [], + "pitchCount": 0, + "phoneticTranscriptions": [], + "context": { + "query": "query", + "fullQuery": "fullQuery", + "document": { + "title": "title" + } + }, + "media": {} + } + ] } ] \ No newline at end of file diff --git a/test/data/translator-test-results.json b/test/data/translator-test-results.json index 50d97775c0..4a81c552bc 100644 --- a/test/data/translator-test-results.json +++ b/test/data/translator-test-results.json @@ -291,7 +291,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -454,7 +459,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -636,7 +646,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -801,7 +816,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -966,7 +986,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -1131,7 +1156,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -1296,7 +1326,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -1459,7 +1494,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -1641,7 +1681,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -1830,7 +1875,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -2019,7 +2069,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -2208,7 +2263,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -2397,8 +2457,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -2564,8 +2629,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -2731,8 +2801,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -2898,8 +2973,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -3065,7 +3145,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -3228,7 +3313,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -3410,7 +3500,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -3523,7 +3618,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -3692,7 +3792,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -3874,7 +3979,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -4039,7 +4149,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -4210,7 +4325,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -4375,7 +4495,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -4546,7 +4671,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -4735,7 +4865,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -4924,8 +5059,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -5091,8 +5231,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -5264,7 +5409,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -5453,7 +5603,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -5642,8 +5797,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -5809,8 +5969,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -5982,7 +6147,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -6105,7 +6275,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -6162,7 +6337,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -6219,7 +6399,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -6276,7 +6461,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -6333,8 +6523,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -6392,8 +6587,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -6451,8 +6651,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -6510,8 +6715,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -6569,7 +6779,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -6626,7 +6841,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -6689,7 +6909,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -6926,7 +7151,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -7163,8 +7393,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -7378,8 +7613,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -7593,7 +7833,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -7756,7 +8001,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -7938,7 +8188,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -8393,8 +8648,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -8803,7 +9063,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -8966,7 +9231,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -9148,10 +9418,15 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "-te", - "progressive or perfect", - "polite past negative" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "-te", + "progressive or perfect", + "polite past negative" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -9341,10 +9616,15 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "-te", - "progressive or perfect", - "polite past negative" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "-te", + "progressive or perfect", + "polite past negative" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -9534,10 +9814,15 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "-te", - "progressive or perfect", - "polite past negative" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "-te", + "progressive or perfect", + "polite past negative" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -9727,10 +10012,15 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "-te", - "progressive or perfect", - "polite past negative" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "-te", + "progressive or perfect", + "polite past negative" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -9920,8 +10210,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -10087,8 +10382,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -10254,8 +10554,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -10421,8 +10726,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -10588,7 +10898,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -10751,7 +11066,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -10933,7 +11253,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -11122,7 +11447,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -11311,7 +11641,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -11500,7 +11835,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -11689,8 +12029,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -11856,8 +12201,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -12023,8 +12373,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -12190,8 +12545,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -12357,7 +12717,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -12520,7 +12885,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -12702,7 +13072,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -12891,7 +13266,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -13080,7 +13460,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -13269,7 +13654,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -13458,8 +13848,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -13625,8 +14020,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -13792,8 +14192,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -13959,8 +14364,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 1, "frequencyOrder": 0, @@ -14126,7 +14536,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -14289,7 +14704,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -14471,8 +14891,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 100, "frequencyOrder": 0, @@ -14576,7 +15001,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 90, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -14679,8 +15109,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "polite past" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "polite past" + ] + } ], "score": 100, "frequencyOrder": 0, @@ -14784,7 +15219,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 10, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -15239,8 +15679,13 @@ { "type": "term", "isPrimary": true, - "inflections": [ - "masu stem" + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [ + "masu stem" + ] + } ], "score": 10, "frequencyOrder": 0, @@ -15655,7 +16100,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -15804,7 +16254,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -15899,7 +16354,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -15994,7 +16454,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -16089,7 +16554,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -16184,7 +16654,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 1, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -16296,7 +16771,12 @@ { "type": "term", "isPrimary": true, - "inflections": [], + "inflectionRuleChainCandidates": [ + { + "source": "algorithm", + "inflectionRules": [] + } + ], "score": 35, "frequencyOrder": 0, "dictionaryIndex": 0, @@ -16399,5 +16879,86 @@ "frequencies": [] } ] + }, + { + "name": "Test dictionary deinflection", + "originalTextLength": 5, + "dictionaryEntries": [ + { + "type": "term", + "isPrimary": true, + "inflectionRuleChainCandidates": [ + { + "source": "both", + "inflectionRules": [ + "past" + ] + } + ], + "score": 1, + "frequencyOrder": 0, + "dictionaryIndex": 0, + "dictionaryPriority": 0, + "sourceTermExactMatchCount": 1, + "maxTransformedTextLength": 5, + "headwords": [ + { + "index": 0, + "term": "のたまう", + "reading": "のたまう", + "sources": [ + { + "originalText": "のたもうた", + "transformedText": "のたもうた", + "deinflectedText": "のたまう", + "matchType": "exact", + "matchSource": "term", + "isPrimary": true + } + ], + "tags": [], + "wordClasses": [ + "v5" + ] + } + ], + "definitions": [ + { + "index": 0, + "headwordIndices": [ + 0 + ], + "dictionary": "Test Dictionary 2", + "dictionaryIndex": 0, + "dictionaryPriority": 0, + "id": 23, + "score": 1, + "frequencyOrder": 0, + "sequences": [ + 15 + ], + "isPrimary": true, + "tags": [ + { + "name": "v5", + "category": "default", + "order": 0, + "score": 0, + "content": [], + "dictionaries": [ + "Test Dictionary 2" + ], + "redundant": false + } + ], + "entries": [ + "notamau definition" + ] + } + ], + "pronunciations": [], + "frequencies": [] + } + ] } ] \ No newline at end of file diff --git a/test/options-util.test.js b/test/options-util.test.js index a34cc93a21..26ccdd14df 100644 --- a/test/options-util.test.js +++ b/test/options-util.test.js @@ -426,7 +426,8 @@ function createProfileOptionsUpdatedTestData1() { enabled: true, allowSecondarySearches: false, definitionsCollapsible: 'not-collapsible', - partsOfSpeechFilter: true + partsOfSpeechFilter: true, + useDeinflections: true } ], parsing: { @@ -603,7 +604,7 @@ function createOptionsUpdatedTestData1() { } ], profileCurrent: 0, - version: 23, + version: 24, global: { database: { prefixWildcardsSupported: false diff --git a/types/ext/anki-templates.d.ts b/types/ext/anki-templates.d.ts index 098873e622..7348b57172 100644 --- a/types/ext/anki-templates.d.ts +++ b/types/ext/anki-templates.d.ts @@ -172,7 +172,7 @@ export type TermDictionaryEntry = { source: string | null; rawSource: string | null; sourceTerm?: string | null; - reasons: string[]; + inflectionRuleChainCandidates: Dictionary.InflectionRuleChainCandidate[]; score: number; isPrimary?: boolean; readonly sequence: number; diff --git a/types/ext/dictionary-data.d.ts b/types/ext/dictionary-data.d.ts index 0e0edd5c46..82e0f00dbe 100644 --- a/types/ext/dictionary-data.d.ts +++ b/types/ext/dictionary-data.d.ts @@ -90,6 +90,11 @@ export type KanjiV3 = [ ]; export type TermGlossary = ( + TermGlossaryContent | + TermGlossaryDeinflection +); + +export type TermGlossaryContent = ( TermGlossaryString | TermGlossaryText | TermGlossaryImage | @@ -100,6 +105,10 @@ export type TermGlossaryString = string; export type TermGlossaryText = {type: 'text', text: string}; export type TermGlossaryImage = {type: 'image'} & TermImage; export type TermGlossaryStructuredContent = {type: 'structured-content', content: StructuredContent.Content}; +export type TermGlossaryDeinflection = [ + uninflected: string, + inflectionRuleChain: string[], +]; export type TermImage = StructuredContent.ImageElementBase & { // Compatibility properties diff --git a/types/ext/dictionary.d.ts b/types/ext/dictionary.d.ts index 7c348e7f5d..ed7769a91e 100644 --- a/types/ext/dictionary.d.ts +++ b/types/ext/dictionary.d.ts @@ -208,9 +208,9 @@ export type TermDictionaryEntry = { */ isPrimary: boolean; /** - * A list of inflections that was applied to get the term. + * Ways that a looked-up word might be an inflected form of this term. */ - inflections: string[]; + inflectionRuleChainCandidates: InflectionRuleChainCandidate[]; /** * A score for the dictionary entry. */ @@ -253,6 +253,15 @@ export type TermDictionaryEntry = { frequencies: TermFrequency[]; }; +export type InflectionRuleChainCandidate = { + source: InflectionSource; + inflectionRules: InflectionRuleChain; +}; + +export type InflectionRuleChain = string[]; + +export type InflectionSource = 'algorithm' | 'dictionary' | 'both'; + /** * A term headword is a combination of a term, reading, and auxiliary information. */ @@ -337,7 +346,7 @@ export type TermDefinition = { /** * The definition entries. */ - entries: DictionaryData.TermGlossary[]; + entries: DictionaryData.TermGlossaryContent[]; }; /** diff --git a/types/ext/settings.d.ts b/types/ext/settings.d.ts index b95691b222..a3d3e141c6 100644 --- a/types/ext/settings.d.ts +++ b/types/ext/settings.d.ts @@ -259,6 +259,7 @@ export type DictionaryOptions = { allowSecondarySearches: boolean; definitionsCollapsible: DictionaryDefinitionsCollapsible; partsOfSpeechFilter: boolean; + useDeinflections: boolean; }; export type ParsingOptions = { diff --git a/types/ext/translation-internal.d.ts b/types/ext/translation-internal.d.ts index 5845b45d44..15456e4196 100644 --- a/types/ext/translation-internal.d.ts +++ b/types/ext/translation-internal.d.ts @@ -16,6 +16,7 @@ */ import type * as DictionaryDatabase from './dictionary-database'; +import type * as Dictionary from './dictionary'; import type * as Translation from './translation'; export type TextDeinflectionOptions = [ @@ -52,7 +53,7 @@ export enum DeinflectionRuleFlags { export type Deinflection = { term: string; rules: DeinflectionRuleFlags; - reasons: string[]; + reasons: Dictionary.InflectionRuleChain; }; export type DatabaseDeinflection = { @@ -60,6 +61,6 @@ export type DatabaseDeinflection = { transformedText: string; deinflectedText: string; rules: DeinflectionRuleFlags; - reasons: string[]; + inflectionRuleChainCandidates: Dictionary.InflectionRuleChainCandidate[]; databaseEntries: DictionaryDatabase.TermEntry[]; }; diff --git a/types/ext/translation.d.ts b/types/ext/translation.d.ts index d597c006a0..db0cf02fec 100644 --- a/types/ext/translation.d.ts +++ b/types/ext/translation.d.ts @@ -177,6 +177,10 @@ export type FindTermDictionary = { * Whether this dictionary's part of speech rules should be used to filter results. */ partsOfSpeechFilter: boolean; + /** + * Whether to use the deinflections from this dictionary. + */ + useDeinflections: boolean; }; export type TermEnabledDictionaryMap = Map;