diff options
29 files changed, 1861 insertions, 342 deletions
| diff --git a/ext/css/display.css b/ext/css/display.css index 49aeaaa5..e0b7ab6d 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 8243f2a7..066229c3 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 8cf00400..24f3a6b0 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 00000000..2288737c --- /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)~}} +        <ul> +            {{~#each definition.phoneticTranscriptions~}} +                {{~#each phoneticTranscriptions~}} +                    <li> +                        {{~set "any" false~}}    +                        {{~#each tags~}} +                            {{~#if (get "any")}}, {{else}}<i>({{/if~}} +                            {{name}} +                            {{~set "any" true~}} +                        {{~/each~}} +                        {{~#if (get "any")}})</i> {{/if~}} +                        {{ipa~}} +                    </li> +                {{~/each~}} +            {{~/each~}} +        </ul> +    {{~/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")~}}<ul>{{/if~}} +            {{~#each definition.inflectionRuleChainCandidates~}} +                {{~#if (op ">" inflectionRules.length 0)~}} +                    {{~#if (get "multiple")~}}<li>{{/if~}} +                    {{~#each inflectionRules~}} +                        {{~#if (op ">" @index 0)}} « {{/if~}} +                        {{.}} +                    {{~/each~}} +                    {{~#if (get "multiple")~}}</li>{{/if~}} +                {{~/if~}} +            {{~/each~}} +        {{~#if (get "multiple")~}}</ul>{{/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 f23b9d0b..818677ce 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")~}}<ul>{{/if~}} +            {{~#each definition.inflectionRuleChainCandidates~}} +                {{~#if (op ">" inflectionRules.length 0)~}} +                    {{~#if (get "multiple")~}}<li>{{/if~}} +                    {{~#each inflectionRules~}} +                        {{~#if (op ">" @index 0)}} « {{/if~}} +                        {{.}} +                    {{~/each~}} +                    {{~#if (get "multiple")~}}</li>{{/if~}} +                {{~/if~}} +            {{~/each~}} +        {{~#if (get "multiple")~}}</ul>{{/if~}}      {{~/if~}}  {{/inline}} diff --git a/ext/display-templates.html b/ext/display-templates.html index ed0037bb..a50cea3b 100644 --- a/ext/display-templates.html +++ b/ext/display-templates.html @@ -32,7 +32,7 @@          <div class="headword-list"></div>          <div class="headword-list-details">              <div class="headword-list-tag-list tag-list"></div> -            <div class="inflection-list"></div> +            <ul class="inflection-rule-chains"></ul>          </div>      </div>      <div class="entry-body"> @@ -77,6 +77,7 @@  <template id="definition-disambiguation-template"><span class="definition-disambiguation"></span></template>  <template id="gloss-item-template"><li class="gloss-item click-scannable"><span class="gloss-separator"> </span><span class="gloss-content"></span></li></template>  <template id="gloss-item-image-description-template"> <span class="gloss-image-description"></span></template> +<template id="inflection-rule-chain-template"><li class="inflection-rule-chain"></li></template>  <template id="inflection-template"><span class="inflection"></span><span class="inflection-separator"> </span></template>  <!-- Frequency templates --> diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index a5a42272..bc4f222f 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 c93e261d..0aabed6f 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); @@ -1156,6 +1157,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<chrome.tabs.Tab>}       */ diff --git a/ext/js/data/sandbox/anki-note-data-creator.js b/ext/js/data/sandbox/anki-note-data-creator.js index c0a11869..77d6e357 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 bfd7a8b2..8df7860e 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 3a2a5621..521cbb41 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); @@ -357,6 +357,44 @@ export class DisplayGenerator {      }      /** +     * @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 d2d92e53..e2b66cb4 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 5441294b..89a3e5ec 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<import('translator').FindTermsResult>}       */ -    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);              } @@ -237,40 +249,163 @@ export class Translator {      }      /** +     * @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<string, import('translation').FindTermDictionary>} enabledDictionaryMap       * @param {import('translation').FindTermsOptions} options       * @returns {Promise<import('translation-internal').DatabaseDeinflection[]>}       */ -    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<string, import('translation').FindTermDictionary>} enabledDictionaryMap +     * @param {import('dictionary').TermSourceMatchType} matchType +     * @returns {Promise<import('translation-internal').DatabaseDeinflection[]>} +     */ +    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<string, import('translation').FindTermDictionary>} 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<string, import('translation-internal').DatabaseDeinflection[]>} +     */ +    _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<string, import('translation').FindTermDictionary>} 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<string, import('translation').FindTermDictionary>} 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 18a802be..1d3da532 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 3311097f..e4822bee 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<string>}       */      _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 2cc521d5..3a4c90ce 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -2691,6 +2691,24 @@              </div>          </div>          <hr> +        <div class="settings-item dictionary-use-deinflections-setting" hidden> +            <div class="settings-item-inner"> +                <div class="settings-item-left"> +                    <div class="settings-item-label"> +                        Use deinflections +                        <a tabindex="0" class="more-toggle more-only" data-parent-distance="4">(?)</a> +                    </div> +                </div> +                <div class="settings-item-right"> +                    <label class="toggle"><input type="checkbox" class="dictionary-use-deinflections-toggle"><span class="toggle-body"><span class="toggle-track"></span><span class="toggle-knob"></span></span></label> +                </div> +            </div> +            <div class="settings-item-children more" hidden> +                Deinflections from this dictionary will be used. +                <p><a tabindex="0" class="more-toggle" data-parent-distance="3">Hide…</a></p> +            </div> +        </div> +        <hr>          <div class="settings-item"><div class="settings-item-children">              <div class="dictionary-details-table"></div>              <div class="dictionary-counts"></div> diff --git a/test/data/anki-note-builder-test-results.json b/test/data/anki-note-builder-test-results.json index 86bffc6a..7129d748 100644 --- a/test/data/anki-note-builder-test-results.json +++ b/test/data/anki-note-builder-test-results.json @@ -2867,5 +2867,41 @@          "url": "<a href=\"url:\">url:</a>"        }      ] +  }, +  { +    "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": "<div style=\"text-align: left;\"><i>(v5, Test Dictionary 2)</i> notamau definition</div>", +        "glossary-brief": "<div style=\"text-align: left;\">notamau definition</div>", +        "glossary-no-dictionary": "<div style=\"text-align: left;\"><i>(v5)</i> notamau definition</div>", +        "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": "<a href=\"url:\">url:</a>" +      } +    ]    }  ]
\ No newline at end of file diff --git a/test/data/database-test-cases.json b/test/data/database-test-cases.json index 02fddd49..611903dd 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 ce4290bd..7f2af6dd 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 91cf0ab2..ce449c1e 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 1342a63f..c34842da 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 50d97775..4a81c552 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 a34cc93a..26ccdd14 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 098873e6..7348b571 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 0e0edd5c..82e0f00d 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 7c348e7f..ed7769a9 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 b95691b2..a3d3e141 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 5845b45d..15456e41 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 d597c006..db0cf02f 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<string, FindTermDictionary>; |