aboutsummaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorStefanVukovic99 <stefanvukovic44@gmail.com>2023-12-28 06:39:19 +0100
committerGitHub <noreply@github.com>2023-12-28 05:39:19 +0000
commitfc2123a45b3ceacc2ec887d24e5e752dca59bb4f (patch)
tree3a5105a6bff7a1755582c0cb9d38996933044b2b /ext
parent60cd218663f62f79394e9c0247e0fe40de6589b6 (diff)
add phonetic transcriptions term meta type (#434)
* move dictionary files to dictionary folder * wip * move dictionary files to dictionary folder * add ipa term meta * wip * fixing comments wip * fixing comments wip * fixing comments wip * fixing comments wip * fixing comments wip * fixing comments wip * fix comments * fix comments * update test data * fix gitignore * engines * add tests * update database test * fix test
Diffstat (limited to 'ext')
-rw-r--r--ext/data/schemas/dictionary-term-meta-bank-v3-schema.json52
-rw-r--r--ext/data/templates/default-anki-field-templates.handlebars21
-rw-r--r--ext/js/data/sandbox/anki-note-data-creator.js102
-rw-r--r--ext/js/dictionary/dictionary-data-util.js93
-rw-r--r--ext/js/dictionary/dictionary-database.js2
-rw-r--r--ext/js/display/display-generator.js64
-rw-r--r--ext/js/language/translator.js44
-rw-r--r--ext/js/pages/settings/anki-controller.js1
-rw-r--r--ext/js/templates/sandbox/anki-template-renderer.js5
9 files changed, 329 insertions, 55 deletions
diff --git a/ext/data/schemas/dictionary-term-meta-bank-v3-schema.json b/ext/data/schemas/dictionary-term-meta-bank-v3-schema.json
index 995c456a..1401b1eb 100644
--- a/ext/data/schemas/dictionary-term-meta-bank-v3-schema.json
+++ b/ext/data/schemas/dictionary-term-meta-bank-v3-schema.json
@@ -40,8 +40,8 @@
},
{
"type": "string",
- "enum": ["freq", "pitch"],
- "description": "Type of data. \"freq\" corresponds to frequency information; \"pitch\" corresponds to pitch information."
+ "enum": ["freq", "pitch", "ipa"],
+ "description": "Type of data. \"freq\" corresponds to frequency information; \"pitch\" corresponds to pitch information. \"ipa\" corresponds to IPA transcription."
},
{
"description": "Data for the term."
@@ -164,6 +164,54 @@
}
}
]
+ },
+ {
+ "minItems": 3,
+ "maxItems": 3,
+ "items": [
+ {},
+ {"const": "ipa"},
+ {
+ "type": ["object"],
+ "description": "IPA transcription information for the term.",
+ "required": [
+ "reading",
+ "transcriptions"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "reading": {
+ "type": "string",
+ "description": "Reading for the term."
+ },
+ "transcriptions": {
+ "type": "array",
+ "description": "List of different IPA transcription information for the term and reading combination.",
+ "items": {
+ "type": "object",
+ "required": [
+ "ipa"
+ ],
+ "additionalProperties": false,
+ "properties": {
+ "ipa": {
+ "type": "string",
+ "description": "IPA transcription for the term."
+ },
+ "tags": {
+ "type": "array",
+ "description": "List of tags for this IPA transcription.",
+ "items": {
+ "type": "string",
+ "description": "Tag for this IPA transcription."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
}
]
}
diff --git a/ext/data/templates/default-anki-field-templates.handlebars b/ext/data/templates/default-anki-field-templates.handlebars
index d94f6d70..f23b9d0b 100644
--- a/ext/data/templates/default-anki-field-templates.handlebars
+++ b/ext/data/templates/default-anki-field-templates.handlebars
@@ -229,6 +229,27 @@
{{/inline}}
{{! End Pitch Accents }}
+{{#*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 "clipboard-image"}}
{{~#if (hasMedia "clipboardImage")~}}
<img src="{{getMedia "clipboardImage"}}" />
diff --git a/ext/js/data/sandbox/anki-note-data-creator.js b/ext/js/data/sandbox/anki-note-data-creator.js
index 9d93b497..c0a11869 100644
--- a/ext/js/data/sandbox/anki-note-data-creator.js
+++ b/ext/js/data/sandbox/anki-note-data-creator.js
@@ -55,6 +55,8 @@ export class AnkiNoteDataCreator {
const context2 = this.createCachedValue(this._getPublicContext.bind(this, context));
const pitches = this.createCachedValue(this._getPitches.bind(this, dictionaryEntry));
const pitchCount = this.createCachedValue(this._getPitchCount.bind(this, pitches));
+ const phoneticTranscriptions = this.createCachedValue(this._getPhoneticTranscriptions.bind(this, dictionaryEntry));
+
if (typeof media !== 'object' || media === null || Array.isArray(media)) {
media = {
audio: void 0,
@@ -82,6 +84,7 @@ export class AnkiNoteDataCreator {
get uniqueReadings() { return self.getCachedValue(uniqueReadings); },
get pitches() { return self.getCachedValue(pitches); },
get pitchCount() { return self.getCachedValue(pitchCount); },
+ get phoneticTranscriptions() { return self.getCachedValue(phoneticTranscriptions); },
get context() { return self.getCachedValue(context2); },
media,
dictionaryEntry
@@ -193,7 +196,11 @@ export class AnkiNoteDataCreator {
for (const {dictionary, pronunciations} of DictionaryDataUtil.getGroupedPronunciations(dictionaryEntry)) {
/** @type {import('anki-templates').Pitch[]} */
const pitches = [];
- for (const {terms, reading, position, nasalPositions, devoicePositions, tags, exclusiveTerms, exclusiveReadings} of pronunciations) {
+ for (const groupedPronunciation of pronunciations) {
+ const {pronunciation} = groupedPronunciation;
+ if (pronunciation.type !== 'pitch-accent') { continue; }
+ const {position, nasalPositions, devoicePositions, tags} = pronunciation;
+ const {terms, reading, exclusiveTerms, exclusiveReadings} = groupedPronunciation;
pitches.push({
expressions: terms,
reading,
@@ -212,6 +219,35 @@ export class AnkiNoteDataCreator {
}
/**
+ * @param {import('dictionary').DictionaryEntry} dictionaryEntry
+ * @returns {import('anki-templates').TranscriptionGroup[]}
+ */
+ _getPhoneticTranscriptions(dictionaryEntry) {
+ const results = [];
+ if (dictionaryEntry.type === 'term') {
+ for (const {dictionary, pronunciations} of DictionaryDataUtil.getGroupedPronunciations(dictionaryEntry)) {
+ const phoneticTranscriptions = [];
+ for (const groupedPronunciation of pronunciations) {
+ const {pronunciation} = groupedPronunciation;
+ if (pronunciation.type !== 'phonetic-transcription') { continue; }
+ const {ipa, tags} = pronunciation;
+ const {terms, reading, exclusiveTerms, exclusiveReadings} = groupedPronunciation;
+ phoneticTranscriptions.push({
+ expressions: terms,
+ reading,
+ ipa,
+ tags,
+ exclusiveExpressions: exclusiveTerms,
+ exclusiveReadings
+ });
+ }
+ results.push({dictionary, phoneticTranscriptions});
+ }
+ }
+ return results;
+ }
+
+ /**
* @param {import('anki-templates-internal').CachedValue<import('anki-templates').PitchGroup[]>} cachedPitches
* @returns {number}
*/
@@ -353,6 +389,7 @@ export class AnkiNoteDataCreator {
const expressions = this.createCachedValue(this._getTermExpressions.bind(this, dictionaryEntry));
const frequencies = this.createCachedValue(this._getTermFrequencies.bind(this, dictionaryEntry));
const pitches = this.createCachedValue(this._getTermPitches.bind(this, dictionaryEntry));
+ const phoneticTranscriptions = this.createCachedValue(this._getTermPhoneticTranscriptions.bind(this, dictionaryEntry));
const glossary = this.createCachedValue(this._getTermGlossaryArray.bind(this, dictionaryEntry, type));
const cloze = this.createCachedValue(this._getCloze.bind(this, dictionaryEntry, context));
const furiganaSegments = this.createCachedValue(this._getTermFuriganaSegments.bind(this, dictionaryEntry, type));
@@ -389,6 +426,7 @@ export class AnkiNoteDataCreator {
get definitions() { return self.getCachedValue(commonInfo).definitions; },
get frequencies() { return self.getCachedValue(frequencies); },
get pitches() { return self.getCachedValue(pitches); },
+ get phoneticTranscriptions() { return self.getCachedValue(phoneticTranscriptions); },
sourceTermExactMatchCount,
url,
get cloze() { return self.getCachedValue(cloze); },
@@ -485,15 +523,16 @@ export class AnkiNoteDataCreator {
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
- * @returns {import('anki-templates').TermPronunciation[]}
+ * @returns {import('anki-templates').TermPitchAccent[]}
*/
_getTermPitches(dictionaryEntry) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
const results = [];
const {headwords} = dictionaryEntry;
- for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pitches} of dictionaryEntry.pronunciations) {
+ for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of dictionaryEntry.pronunciations) {
const {term, reading} = headwords[headwordIndex];
+ const pitches = DictionaryDataUtil.getPronunciationsOfType(pronunciations, 'pitch-accent');
const cachedPitches = this.createCachedValue(this._getTermPitchesInner.bind(this, pitches));
results.push({
index: results.length,
@@ -512,8 +551,8 @@ export class AnkiNoteDataCreator {
}
/**
- * @param {import('dictionary').TermPitch[]} pitches
- * @returns {import('anki-templates').TermPitch[]}
+ * @param {import('dictionary').PitchAccent[]} pitches
+ * @returns {import('anki-templates').PitchAccent[]}
*/
_getTermPitchesInner(pitches) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
@@ -531,6 +570,52 @@ export class AnkiNoteDataCreator {
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
+ * @returns {import('anki-templates').TermPhoneticTranscription[]}
+ */
+ _getTermPhoneticTranscriptions(dictionaryEntry) {
+ const results = [];
+ const {headwords} = dictionaryEntry;
+ for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of dictionaryEntry.pronunciations) {
+ const {term, reading} = headwords[headwordIndex];
+ const phoneticTranscriptions = DictionaryDataUtil.getPronunciationsOfType(pronunciations, 'phonetic-transcription');
+ const termPhoneticTranscriptions = this._getTermPhoneticTranscriptionsInner(phoneticTranscriptions);
+ results.push({
+ index: results.length,
+ expressionIndex: headwordIndex,
+ dictionary,
+ dictionaryOrder: {
+ index: dictionaryIndex,
+ priority: dictionaryPriority
+ },
+ expression: term,
+ reading,
+ get phoneticTranscriptions() { return termPhoneticTranscriptions; }
+ });
+ }
+
+ return results;
+ }
+
+ /**
+ * @param {import('dictionary').PhoneticTranscription[]} phoneticTranscriptions
+ * @returns {import('anki-templates').PhoneticTranscription[]}
+ */
+ _getTermPhoneticTranscriptionsInner(phoneticTranscriptions) {
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
+ const self = this;
+ const results = [];
+ for (const {ipa, tags} of phoneticTranscriptions) {
+ const cachedTags = this.createCachedValue(this._convertTags.bind(this, tags));
+ results.push({
+ ipa,
+ get tags() { return self.getCachedValue(cachedTags); }
+ });
+ }
+ return results;
+ }
+
+ /**
+ * @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @returns {import('anki-templates').TermHeadword[]}
*/
_getTermExpressions(dictionaryEntry) {
@@ -592,16 +677,17 @@ export class AnkiNoteDataCreator {
/**
* @param {import('dictionary').TermDictionaryEntry} dictionaryEntry
* @param {number} i
- * @returns {import('anki-templates').TermPronunciation[]}
+ * @returns {import('anki-templates').TermPitchAccent[]}
*/
_getTermExpressionPitches(dictionaryEntry, i) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
const results = [];
- const {headwords, pronunciations} = dictionaryEntry;
- for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pitches} of pronunciations) {
+ const {headwords, pronunciations: termPronunciations} = dictionaryEntry;
+ for (const {headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations} of termPronunciations) {
if (headwordIndex !== i) { continue; }
const {term, reading} = headwords[headwordIndex];
+ const pitches = DictionaryDataUtil.getPronunciationsOfType(pronunciations, 'pitch-accent');
const cachedPitches = this.createCachedValue(this._getTermPitchesInner.bind(this, pitches));
results.push({
index: results.length,
diff --git a/ext/js/dictionary/dictionary-data-util.js b/ext/js/dictionary/dictionary-data-util.js
index a54b043b..50ae4b11 100644
--- a/ext/js/dictionary/dictionary-data-util.js
+++ b/ext/js/dictionary/dictionary-data-util.js
@@ -135,7 +135,7 @@ export class DictionaryDataUtil {
* @returns {import('dictionary-data-util').DictionaryGroupedPronunciations[]}
*/
static getGroupedPronunciations(dictionaryEntry) {
- const {headwords, pronunciations} = dictionaryEntry;
+ const {headwords, pronunciations: termPronunciations} = dictionaryEntry;
const allTerms = new Set();
const allReadings = new Set();
@@ -146,23 +146,20 @@ export class DictionaryDataUtil {
/** @type {Map<string, import('dictionary-data-util').GroupedPronunciationInternal[]>} */
const groupedPronunciationsMap = new Map();
- for (const {headwordIndex, dictionary, pitches} of pronunciations) {
+ for (const {headwordIndex, dictionary, pronunciations} of termPronunciations) {
const {term, reading} = headwords[headwordIndex];
let dictionaryGroupedPronunciationList = groupedPronunciationsMap.get(dictionary);
if (typeof dictionaryGroupedPronunciationList === 'undefined') {
dictionaryGroupedPronunciationList = [];
groupedPronunciationsMap.set(dictionary, dictionaryGroupedPronunciationList);
}
- for (const {position, nasalPositions, devoicePositions, tags} of pitches) {
- let groupedPronunciation = this._findExistingGroupedPronunciation(reading, position, nasalPositions, devoicePositions, tags, dictionaryGroupedPronunciationList);
+ for (const pronunciation of pronunciations) {
+ let groupedPronunciation = this._findExistingGroupedPronunciation(reading, pronunciation, dictionaryGroupedPronunciationList);
if (groupedPronunciation === null) {
groupedPronunciation = {
+ pronunciation,
terms: new Set(),
- reading,
- position,
- nasalPositions,
- devoicePositions,
- tags
+ reading
};
dictionaryGroupedPronunciationList.push(groupedPronunciation);
}
@@ -177,29 +174,44 @@ export class DictionaryDataUtil {
/** @type {import('dictionary-data-util').GroupedPronunciation[]} */
const pronunciations2 = [];
for (const groupedPronunciation of dictionaryGroupedPronunciationList) {
- const {terms, reading, position, nasalPositions, devoicePositions, tags} = groupedPronunciation;
+ const {pronunciation, terms, reading} = groupedPronunciation;
const exclusiveTerms = !this._areSetsEqual(terms, allTerms) ? this._getSetIntersection(terms, allTerms) : [];
const exclusiveReadings = [];
if (multipleReadings) {
exclusiveReadings.push(reading);
}
pronunciations2.push({
+ pronunciation,
terms: [...terms],
reading,
- position,
- nasalPositions,
- devoicePositions,
- tags,
exclusiveTerms,
exclusiveReadings
});
}
+
results2.push({dictionary, pronunciations: pronunciations2});
}
return results2;
}
/**
+ * @template {import('dictionary').PronunciationType} T
+ * @param {import('dictionary').Pronunciation[]} pronunciations
+ * @param {T} type
+ * @returns {import('dictionary').PronunciationGeneric<T>[]}
+ */
+ static getPronunciationsOfType(pronunciations, type) {
+ /** @type {import('dictionary').PronunciationGeneric<T>[]} */
+ const results = [];
+ for (const pronunciation of pronunciations) {
+ if (pronunciation.type !== type) { continue; }
+ // This is type safe, but for some reason the cast is needed.
+ results.push(/** @type {import('dictionary').PronunciationGeneric<T>} */ (pronunciation));
+ }
+ return results;
+ }
+
+ /**
* @param {import('dictionary').Tag[]|import('anki-templates').Tag[]} termTags
* @returns {import('dictionary-data-util').TermFrequencyType}
*/
@@ -288,26 +300,49 @@ export class DictionaryDataUtil {
/**
* @param {string} reading
- * @param {number} position
- * @param {number[]} nasalPositions
- * @param {number[]} devoicePositions
- * @param {import('dictionary').Tag[]} tags
+ * @param {import('dictionary').Pronunciation} pronunciation
* @param {import('dictionary-data-util').GroupedPronunciationInternal[]} groupedPronunciationList
* @returns {?import('dictionary-data-util').GroupedPronunciationInternal}
*/
- static _findExistingGroupedPronunciation(reading, position, nasalPositions, devoicePositions, tags, groupedPronunciationList) {
- for (const pitchInfo of groupedPronunciationList) {
- if (
- pitchInfo.reading === reading &&
- pitchInfo.position === position &&
- this._areArraysEqual(pitchInfo.nasalPositions, nasalPositions) &&
- this._areArraysEqual(pitchInfo.devoicePositions, devoicePositions) &&
- this._areTagListsEqual(pitchInfo.tags, tags)
- ) {
- return pitchInfo;
+ static _findExistingGroupedPronunciation(reading, pronunciation, groupedPronunciationList) {
+ const existingGroupedPronunciation = groupedPronunciationList.find((groupedPronunciation) => {
+ return groupedPronunciation.reading === reading && this._arePronunciationsEquivalent(groupedPronunciation, pronunciation);
+ });
+
+ return existingGroupedPronunciation || null;
+ }
+
+ /**
+ * @param {import('dictionary-data-util').GroupedPronunciationInternal} groupedPronunciation
+ * @param {import('dictionary').Pronunciation} pronunciation2
+ * @returns {boolean}
+ */
+ static _arePronunciationsEquivalent({pronunciation: pronunciation1}, pronunciation2) {
+ if (
+ pronunciation1.type !== pronunciation2.type ||
+ !this._areTagListsEqual(pronunciation1.tags, pronunciation2.tags)
+ ) {
+ return false;
+ }
+ switch (pronunciation1.type) {
+ case 'pitch-accent':
+ {
+ // This cast is valid based on the type check at the start of the function.
+ const pitchAccent2 = /** @type {import('dictionary').PitchAccent} */ (pronunciation2);
+ return (
+ pronunciation1.position === pitchAccent2.position &&
+ this._areArraysEqual(pronunciation1.nasalPositions, pitchAccent2.nasalPositions) &&
+ this._areArraysEqual(pronunciation1.devoicePositions, pitchAccent2.devoicePositions)
+ );
+ }
+ case 'phonetic-transcription':
+ {
+ // This cast is valid based on the type check at the start of the function.
+ const phoneticTranscription2 = /** @type {import('dictionary').PhoneticTranscription} */ (pronunciation2);
+ return pronunciation1.ipa === phoneticTranscription2.ipa;
}
}
- return null;
+ return true;
}
/**
diff --git a/ext/js/dictionary/dictionary-database.js b/ext/js/dictionary/dictionary-database.js
index 45c5c6fd..02db6322 100644
--- a/ext/js/dictionary/dictionary-database.js
+++ b/ext/js/dictionary/dictionary-database.js
@@ -627,6 +627,8 @@ export class DictionaryDatabase {
return {index, term, mode, data, dictionary};
case 'pitch':
return {index, term, mode, data, dictionary};
+ case 'ipa':
+ return {index, term, mode, data, dictionary};
default:
throw new Error(`Unknown mode: ${mode}`);
}
diff --git a/ext/js/display/display-generator.js b/ext/js/display/display-generator.js
index b91d0ce9..3a2a5621 100644
--- a/ext/js/display/display-generator.js
+++ b/ext/js/display/display-generator.js
@@ -626,7 +626,7 @@ export class DisplayGenerator {
n1.appendChild(tag);
let hasTags = false;
- for (const {tags} of pronunciations) {
+ for (const {pronunciation: {tags}} of pronunciations) {
if (tags.length > 0) {
hasTags = true;
break;
@@ -645,8 +645,52 @@ export class DisplayGenerator {
* @returns {HTMLElement}
*/
_createPronunciation(details) {
+ const {pronunciation} = details;
+ switch (pronunciation.type) {
+ case 'pitch-accent':
+ return this._createPronunciationPitchAccent(pronunciation, details);
+ case 'phonetic-transcription':
+ return this._createPronunciationPhoneticTranscription(pronunciation, details);
+ }
+ }
+
+
+ /**
+ * @param {import('dictionary').PhoneticTranscription} pronunciation
+ * @param {import('dictionary-data-util').GroupedPronunciation} details
+ * @returns {HTMLElement}
+ */
+ _createPronunciationPhoneticTranscription(pronunciation, details) {
+ const {ipa, tags} = pronunciation;
+ const {exclusiveTerms, exclusiveReadings} = details;
+
+ const node = this._instantiate('pronunciation');
+
+ node.dataset.tagCount = `${tags.length}`;
+
+ let n = this._querySelector(node, '.pronunciation-tag-list');
+ this._appendMultiple(n, this._createTag.bind(this), tags);
+
+ n = this._querySelector(node, '.pronunciation-disambiguation-list');
+ this._createPronunciationDisambiguations(n, exclusiveTerms, exclusiveReadings);
+
+ n = this._querySelector(node, '.pronunciation-text-container');
+
+ this._setTextContent(n, ipa);
+
+ return node;
+ }
+
+ /**
+ * @param {import('dictionary').PitchAccent} pitchAccent
+ * @param {import('dictionary-data-util').GroupedPronunciation} details
+ * @returns {HTMLElement}
+ */
+ _createPronunciationPitchAccent(pitchAccent, details) {
const jp = this._japaneseUtil;
- const {reading, position, nasalPositions, devoicePositions, tags, exclusiveTerms, exclusiveReadings} = details;
+
+ const {position, nasalPositions, devoicePositions, tags} = pitchAccent;
+ const {reading, exclusiveTerms, exclusiveReadings} = details;
const morae = jp.getKanaMorae(reading);
const node = this._instantiate('pronunciation');
@@ -666,6 +710,7 @@ export class DisplayGenerator {
n.appendChild(this._pronunciationGenerator.createPronunciationDownstepPosition(position));
n = this._querySelector(node, '.pronunciation-text-container');
+
n.lang = 'ja';
n.appendChild(this._pronunciationGenerator.createPronunciationText(morae, position, nasalPositions, devoicePositions));
@@ -954,20 +999,21 @@ export class DisplayGenerator {
/**
* @param {string} reading
- * @param {import('dictionary').TermPronunciation[]} pronunciations
+ * @param {import('dictionary').TermPronunciation[]} termPronunciations
* @param {string[]} wordClasses
* @param {number} headwordIndex
* @returns {?string}
*/
- _getPronunciationCategories(reading, pronunciations, wordClasses, headwordIndex) {
- if (pronunciations.length === 0) { return null; }
+ _getPronunciationCategories(reading, termPronunciations, wordClasses, headwordIndex) {
+ if (termPronunciations.length === 0) { return null; }
const isVerbOrAdjective = DictionaryDataUtil.isNonNounVerbOrAdjective(wordClasses);
/** @type {Set<import('japanese-util').PitchCategory>} */
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, isVerbOrAdjective);
+ for (const termPronunciation of termPronunciations) {
+ if (termPronunciation.headwordIndex !== headwordIndex) { continue; }
+ for (const pronunciation of termPronunciation.pronunciations) {
+ if (pronunciation.type !== 'pitch-accent') { continue; }
+ const category = this._japaneseUtil.getPitchCategory(reading, pronunciation.position, isVerbOrAdjective);
if (category !== null) {
categories.add(category);
}
diff --git a/ext/js/language/translator.js b/ext/js/language/translator.js
index 45909940..733955c2 100644
--- a/ext/js/language/translator.js
+++ b/ext/js/language/translator.js
@@ -964,7 +964,7 @@ export class Translator {
case 'pitch':
{
if (data.reading !== reading) { continue; }
- /** @type {import('dictionary').TermPitch[]} */
+ /** @type {import('dictionary').PitchAccent[]} */
const pitches = [];
for (const {position, tags, nasal, devoice} of data.pitches) {
/** @type {import('dictionary').Tag[]} */
@@ -974,7 +974,13 @@ export class Translator {
}
const nasalPositions = this._toNumberArray(nasal);
const devoicePositions = this._toNumberArray(devoice);
- pitches.push({position, nasalPositions, devoicePositions, tags: tags2});
+ pitches.push({
+ type: 'pitch-accent',
+ position,
+ nasalPositions,
+ devoicePositions,
+ tags: tags2
+ });
}
for (const {pronunciations, headwordIndex} of targets) {
pronunciations.push(this._createTermPronunciation(
@@ -988,6 +994,34 @@ export class Translator {
}
}
break;
+ case 'ipa':
+ {
+ if (data.reading !== reading) { continue; }
+ /** @type {import('dictionary').PhoneticTranscription[]} */
+ const phoneticTranscriptions = [];
+ for (const {ipa, tags} of data.transcriptions) {
+ /** @type {import('dictionary').Tag[]} */
+ const tags2 = [];
+ if (Array.isArray(tags)) {
+ tagAggregator.addTags(tags2, dictionary, tags);
+ }
+ phoneticTranscriptions.push({
+ type: 'phonetic-transcription',
+ ipa,
+ tags: tags2
+ });
+ }
+ for (const {pronunciations, headwordIndex} of targets) {
+ pronunciations.push(this._createTermPronunciation(
+ pronunciations.length,
+ headwordIndex,
+ dictionary,
+ dictionaryIndex,
+ dictionaryPriority,
+ phoneticTranscriptions
+ ));
+ }
+ }
}
}
}
@@ -1341,11 +1375,11 @@ export class Translator {
* @param {string} dictionary
* @param {number} dictionaryIndex
* @param {number} dictionaryPriority
- * @param {import('dictionary').TermPitch[]} pitches
+ * @param {import('dictionary').Pronunciation[]} pronunciations
* @returns {import('dictionary').TermPronunciation}
*/
- _createTermPronunciation(index, headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pitches) {
- return {index, headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pitches};
+ _createTermPronunciation(index, headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations) {
+ return {index, headwordIndex, dictionary, dictionaryIndex, dictionaryPriority, pronunciations};
}
/**
diff --git a/ext/js/pages/settings/anki-controller.js b/ext/js/pages/settings/anki-controller.js
index 6f357680..aea94b65 100644
--- a/ext/js/pages/settings/anki-controller.js
+++ b/ext/js/pages/settings/anki-controller.js
@@ -145,6 +145,7 @@ export class AnkiController {
'pitch-accents',
'pitch-accent-graphs',
'pitch-accent-positions',
+ 'phonetic-transcriptions',
'reading',
'screenshot',
'search-query',
diff --git a/ext/js/templates/sandbox/anki-template-renderer.js b/ext/js/templates/sandbox/anki-template-renderer.js
index 57725bcb..15810239 100644
--- a/ext/js/templates/sandbox/anki-template-renderer.js
+++ b/ext/js/templates/sandbox/anki-template-renderer.js
@@ -543,12 +543,13 @@ export class AnkiTemplateRenderer {
const [data] = /** @type {[data: import('anki-templates').NoteData]} */ (args);
const {dictionaryEntry} = data;
if (dictionaryEntry.type !== 'term') { return []; }
- const {pronunciations, headwords} = dictionaryEntry;
+ const {pronunciations: termPronunciations, headwords} = dictionaryEntry;
/** @type {Set<string>} */
const categories = new Set();
- for (const {headwordIndex, pitches} of pronunciations) {
+ for (const {headwordIndex, pronunciations} of termPronunciations) {
const {reading, wordClasses} = headwords[headwordIndex];
const isVerbOrAdjective = DictionaryDataUtil.isNonNounVerbOrAdjective(wordClasses);
+ const pitches = DictionaryDataUtil.getPronunciationsOfType(pronunciations, 'pitch-accent');
for (const {position} of pitches) {
const category = this._japaneseUtil.getPitchCategory(reading, position, isVerbOrAdjective);
if (category !== null) {