From 90f7d5ba07340413aa7e43c3a0cc038690b32db3 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 26 Mar 2021 19:50:54 -0400 Subject: Add part of speech info (#1561) * Add part of speech info to headwords * Expose parts of speech to Anki template rendering * Expose parts of speech * Update pitch accent categories * Update docs * Add part-of-speech * Update options and tests * Update markers * Update test data --- .../anki-field-templates-upgrade-v10.handlebars | 30 +++++++++++++++++++++ .../default-anki-field-templates.handlebars | 31 ++++++++++++++++++++++ ext/js/data/anki-note-data-creator.js | 5 ++-- ext/js/data/options-util.js | 6 +++-- ext/js/display/display-generator.js | 26 +++++++++++++++--- ext/js/language/translator.js | 13 ++++----- ext/js/pages/settings/anki-controller.js | 1 + ext/settings.html | 4 +++ 8 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 ext/data/templates/anki-field-templates-upgrade-v10.handlebars (limited to 'ext') diff --git a/ext/data/templates/anki-field-templates-upgrade-v10.handlebars b/ext/data/templates/anki-field-templates-upgrade-v10.handlebars new file mode 100644 index 00000000..8442a93c --- /dev/null +++ b/ext/data/templates/anki-field-templates-upgrade-v10.handlebars @@ -0,0 +1,30 @@ +{{#*inline "part-of-speech-pretty"}} + {{~#if (op "===" . "v1")~}}Ichidan verb + {{~else if (op "===" . "v5")~}}Godan verb + {{~else if (op "===" . "vk")~}}Kuru verb + {{~else if (op "===" . "vs")~}}Suru verb + {{~else if (op "===" . "vz")~}}Zuru verb + {{~else if (op "===" . "adj-i")~}}I-adjective + {{~else if (op "===" . "n")~}}Noun + {{~else~}}{{.}} + {{~/if~}} +{{/inline}} + +{{#*inline "part-of-speech"}} + {{~#scope~}} + {{~#if (op "!==" definition.type "kanji")~}} + {{~#set "first" true}}{{/set~}} + {{~#each definition.expressions~}} + {{~#each wordClasses~}} + {{~#unless (get (concat "used_" .))~}} + {{~> part-of-speech-pretty . ~}} + {{~#unless (get "first")}}, {{/unless~}} + {{~#set (concat "used_" .) true~}}{{~/set~}} + {{~#set "first" false~}}{{~/set~}} + {{~/unless~}} + {{~/each~}} + {{~/each~}} + {{~#if (get "first")~}}Unknown{{~/if~}} + {{~/if~}} + {{~/scope~}} +{{/inline}} diff --git a/ext/data/templates/default-anki-field-templates.handlebars b/ext/data/templates/default-anki-field-templates.handlebars index 0f390e8d..ec57a826 100644 --- a/ext/data/templates/default-anki-field-templates.handlebars +++ b/ext/data/templates/default-anki-field-templates.handlebars @@ -341,4 +341,35 @@ {{~/scope~}} {{/inline}} +{{#*inline "part-of-speech-pretty"}} + {{~#if (op "===" . "v1")~}}Ichidan verb + {{~else if (op "===" . "v5")~}}Godan verb + {{~else if (op "===" . "vk")~}}Kuru verb + {{~else if (op "===" . "vs")~}}Suru verb + {{~else if (op "===" . "vz")~}}Zuru verb + {{~else if (op "===" . "adj-i")~}}I-adjective + {{~else if (op "===" . "n")~}}Noun + {{~else~}}{{.}} + {{~/if~}} +{{/inline}} + +{{#*inline "part-of-speech"}} + {{~#scope~}} + {{~#if (op "!==" definition.type "kanji")~}} + {{~#set "first" true}}{{/set~}} + {{~#each definition.expressions~}} + {{~#each wordClasses~}} + {{~#unless (get (concat "used_" .))~}} + {{~> part-of-speech-pretty . ~}} + {{~#unless (get "first")}}, {{/unless~}} + {{~#set (concat "used_" .) true~}}{{~/set~}} + {{~#set "first" false~}}{{~/set~}} + {{~/unless~}} + {{~/each~}} + {{~/each~}} + {{~#if (get "first")~}}Unknown{{~/if~}} + {{~/if~}} + {{~/scope~}} +{{/inline}} + {{~> (lookup . "marker") ~}} diff --git a/ext/js/data/anki-note-data-creator.js b/ext/js/data/anki-note-data-creator.js index c7047633..fb9c8b8c 100644 --- a/ext/js/data/anki-note-data-creator.js +++ b/ext/js/data/anki-note-data-creator.js @@ -441,7 +441,7 @@ class AnkiNoteDataCreator { const results = []; const {headwords} = dictionaryEntry; for (let i = 0, ii = headwords.length; i < ii; ++i) { - const {term, reading, tags, sources: [{deinflectedText}]} = headwords[i]; + const {term, reading, tags, sources: [{deinflectedText}], wordClasses} = headwords[i]; const termTags = this.createCachedValue(this._convertTags.bind(this, tags)); const frequencies = this.createCachedValue(this._getTermExpressionFrequencies.bind(this, dictionaryEntry, i)); const pitches = this.createCachedValue(this._getTermExpressionPitches.bind(this, dictionaryEntry, i)); @@ -455,7 +455,8 @@ class AnkiNoteDataCreator { get frequencies() { return self.getCachedValue(frequencies); }, get pitches() { return self.getCachedValue(pitches); }, get furiganaSegments() { return self.getCachedValue(furiganaSegments); }, - get termFrequency() { return self.getCachedValue(termFrequency); } + get termFrequency() { return self.getCachedValue(termFrequency); }, + wordClasses }; results.push(item); } diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index 334445c6..fd62a558 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -459,7 +459,7 @@ class OptionsUtil { {async: false, update: this._updateVersion7.bind(this)}, {async: true, update: this._updateVersion8.bind(this)}, {async: false, update: this._updateVersion9.bind(this)}, - {async: false, update: this._updateVersion10.bind(this)} + {async: true, update: this._updateVersion10.bind(this)} ]; } @@ -750,9 +750,11 @@ class OptionsUtil { return options; } - _updateVersion10(options) { + async _updateVersion10(options) { // Version 10 changes: // Removed global option useSettingsV2. + // Added part-of-speech field template. + await this._applyAnkiFieldTemplatesPatch(options, '/data/templates/anki-field-templates-upgrade-v10.handlebars'); delete options.global.useSettingsV2; return options; } diff --git a/ext/js/display/display-generator.js b/ext/js/display/display-generator.js index 19e88b8a..724bec9c 100644 --- a/ext/js/display/display-generator.js +++ b/ext/js/display/display-generator.js @@ -250,10 +250,14 @@ class DisplayGenerator { node.dataset.readingIsSame = `${reading === expression}`; node.dataset.frequency = DictionaryDataUtil.getTermFrequency(termTags); - const pitchAccentCategories = this._getPitchAccentCategories(reading, pronunciations, headwordIndex); + const {wordClasses} = headword; + const pitchAccentCategories = this._getPitchAccentCategories(reading, pronunciations, wordClasses, headwordIndex); if (pitchAccentCategories !== null) { node.dataset.pitchAccentCategories = pitchAccentCategories; } + if (wordClasses.length > 0) { + node.dataset.wordClasses = wordClasses.join(' '); + } this._setTextContent(node.querySelector('.expression-reading'), reading); @@ -762,13 +766,14 @@ class DisplayGenerator { } } - _getPitchAccentCategories(reading, pronunciations, headwordIndex) { + _getPitchAccentCategories(reading, pronunciations, wordClasses, headwordIndex) { if (pronunciations.length === 0) { return null; } + const isVerbOrAdjective = this._isVerbOrAdjective(wordClasses); const categories = new Set(); for (const pronunciation of pronunciations) { if (pronunciation.headwordIndex !== headwordIndex) { continue; } for (const {position} of pronunciation.pitches) { - const category = this._japaneseUtil.getPitchCategory(reading, position, false); + const category = this._japaneseUtil.getPitchCategory(reading, position, isVerbOrAdjective); if (category !== null) { categories.add(category); } @@ -776,4 +781,19 @@ class DisplayGenerator { } return categories.size > 0 ? [...categories].join(' ') : null; } + + _isVerbOrAdjective(wordClasses) { + for (const wordClass of wordClasses) { + switch (wordClass) { + case 'v1': + case 'v5': + case 'vs': + case 'vk': + case 'vz': + case 'adj-i': + return true; + } + } + return false; + } } diff --git a/ext/js/language/translator.js b/ext/js/language/translator.js index ad9689ee..e99ac8db 100644 --- a/ext/js/language/translator.js +++ b/ext/js/language/translator.js @@ -917,8 +917,8 @@ class Translator { return {originalText, transformedText, deinflectedText, isPrimary}; } - _createTermHeadword(index, term, reading, sources, tags) { - return {index, term, reading, sources, tags}; + _createTermHeadword(index, term, reading, sources, tags, wordClasses) { + return {index, term, reading, sources, tags, wordClasses}; } _createTermDefinition(index, headwordIndices, dictionary, tags, entries) { @@ -953,7 +953,7 @@ class Translator { } _createTermDictionaryEntryFromDatabaseEntry(databaseEntry, originalText, transformedText, deinflectedText, reasons, isPrimary, enabledDictionaryMap) { - const {expression, reading: rawReading, definitionTags, termTags, glossary, score, dictionary, id, sequence} = databaseEntry; + const {expression, reading: rawReading, definitionTags, termTags, glossary, score, dictionary, id, sequence, rules} = databaseEntry; const reading = (rawReading.length > 0 ? rawReading : expression); const {index: dictionaryIndex, priority: dictionaryPriority} = this._getDictionaryOrder(dictionary, enabledDictionaryMap); const sourceTermExactMatchCount = (isPrimary && deinflectedText === expression ? 1 : 0); @@ -975,7 +975,7 @@ class Translator { dictionaryPriority, sourceTermExactMatchCount, maxTransformedTextLength, - [this._createTermHeadword(0, expression, reading, [source], headwordTagGroups)], + [this._createTermHeadword(0, expression, reading, [source], headwordTagGroups, rules)], [this._createTermDefinition(0, [0], dictionary, definitionTagGroups, glossary)] ); } @@ -1100,15 +1100,16 @@ class Translator { _addTermHeadwords(headwordsMap, headwords) { const headwordIndexMap = []; - for (const {term, reading, sources, tags} of headwords) { + for (const {term, reading, sources, tags, wordClasses} of headwords) { const key = this._createMapKey([term, reading]); let headword = headwordsMap.get(key); if (typeof headword === 'undefined') { - headword = this._createTermHeadword(headwordsMap.size, term, reading, [], []); + headword = this._createTermHeadword(headwordsMap.size, term, reading, [], [], []); headwordsMap.set(key, headword); } this._addUniqueSources(headword.sources, sources); this._addUniqueTagGroups(headword.tags, tags); + this._addUniqueStrings(headword.wordClasses, wordClasses); headwordIndexMap.push(headword.index); } return headwordIndexMap; diff --git a/ext/js/pages/settings/anki-controller.js b/ext/js/pages/settings/anki-controller.js index 37ae2cf7..4e56594a 100644 --- a/ext/js/pages/settings/anki-controller.js +++ b/ext/js/pages/settings/anki-controller.js @@ -99,6 +99,7 @@ class AnkiController { 'glossary', 'glossary-brief', 'glossary-no-dictionary', + 'part-of-speech', 'pitch-accents', 'pitch-accent-graphs', 'pitch-accent-positions', diff --git a/ext/settings.html b/ext/settings.html index a9c29a73..2f8c012c 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -2651,6 +2651,10 @@ {glossary-no-dictionary} List of definitions for the term, except the dictionary tag is omitted. + + {part-of-speech} + Part of speech information for the term. + {pitch-accents} List of pitch accent downstep notations for the term. -- cgit v1.2.3